home *** CD-ROM | disk | FTP | other *** search
Text File | 1995-12-04 | 93.9 KB | 3,063 lines | [TEXT/CWIE] |
- /* See the file Distribution for distribution terms.
- (c) Copyright 1994 Ari Halberstadt */
-
- /* Functions implementing a popup menu. To create a popup call
- PopupBegin and then PopupCurrentSet to set the initial item.
- Use PopupCurrent to find out which item was selected. There
- are quite a few other functions for controling how the popup
- is displayed: whether the popup is drawn, whether the popup
- is visible, enabling and disabling the popup, whether to
- use the window font to display the popup, setting the text
- style for the title, and more. The defaults are set by
- PopupBegin to be those recommended by Apple in IM-VI. This
- library is the basis for a popup menu 'CDEF'.
-
- You can use the popup CDEF to create a popup menu. The control's
- contrlData field will contain a handle to the popup. If you need to
- directly modify the popup, you can call the popup library routines
- on the handle. However, first call PopupVersion and make sure it
- is equal to kPopupVersion. If it isn't, then don't use the popup
- library to modify the control. In practice, it is better to use
- Control Manager routines to modify the popup.
-
- If you implement keyboard equivalents of menu commands, then you can
- use PopupHilite to hilite the title of the menu while executing the
- menu command.
-
- 950828 aih
- - fixed test from PopupMenuSelect -- checks HiWord, not LoWord
- - PopupSelect returns a different part code if no selection is made
- - MenuDisable is set to zero before PopupMenuSelect is called (so can
- test for user not selecting any menu item)
- - removed toggle attribute
- - tests for grayishTextOr using gestalt
- - uses onscreen port's (possibly non-white) bkPixPat when drawing offscreen,
- yet allows to override using a menu or control color table, and inversion
- of title works reasonably well
- - popup can be beveled
-
- 950824 aih
- - title's height is calculated using the style attributes in which it will
- be drawn
-
- 950716 aih
- - added glue for mdef patch for compiling as native ppc
-
- 950707 aih
- - font script for a menu item is set even if use window font is true
- - found bug in type-in popup menu size calculation, if menu was not
- empty and type-in attribute was true, triangle's size would be incorrect
-
- 950629 aih
- - restores menuProc field before calling original menuProc
-
- 950621 aih
- - clip region is intersected with port's clip region, so popup
- CDEF doesn't draw outside of clip region
- - added routine descriptor for device loop callback, so can
- compile for PPC
- - added toggle attribute
-
- 941126 aih
- - Supports control color tables.
- - Popup title colors are swapped when hiliting the
- title. This is how the system MDEF and other CDEFs
- behave. The title is simply inverted when drawing in
- black and white.
- - Removed support for old headers (pre-Universal Headers).
-
- 940908 aih
- - Fixed disabling of items > 15.
-
- 940716 aih
- - Fixed almost all color problems.
- - Improved right justification of menus, depending on system
- justification value.
-
- 940712 aih
- - Adapted for color QuickDraw and offscreen graphics worlds.
- - Added support for color menu entries.
-
- 940706 aih
- - Adapted for universal headers
-
- 940315 aih
- - The current selection will display items with small icons properly.
- Formerly, this would display a large icon for items that had small or
- reduced icons, and wouldn't display the large icon once a different icon
- had been selected. Reduced icons still aren't displayed in the current
- selection, and I'm not sure why not, since I copy the command key character
- for reduced icons (0x1D) to the menu item used to draw the current
- selection.
- - When the useWFont variation code is used, the menu will be drawn in
- the font and size of the popup menu's port. Formerly, using the useWFont
- variation code produced incorrect results, but I fixed this by setting the
- system font and size low-memory globals instead of trying to change the
- font and size of the Window Manager's port.
- - The currently selected item is drawn in gray if it is disabled. Formerly,
- the item was only drawn in gray if it was disabled and if the menu item
- was drawn using the MDEF.
- - Updated "To Do" list.
- - Clicking in the popup's title will also pull down the menu. This
- is consistent with the operation of the system 7 popup CDEF.
- - Added support for the popupFixedWidth variation code.
-
- 931228 aih
- - Continued cleaning up code, added some more comments.
- - Uses menu definition function to draw the current selection. This
- eliminated a couple of rectangles from the popup structure and
- means that the current selection will always be drawn correctly,
- even if it includes icons and command keys. A special one item menu
- is used to hold a copy of the currently selected menu item. This special
- menu is then used to calculate the height of the item and to draw the
- item.
-
- 931226 aih
- - Major overhaul. Rewrote rectangle calculation code, since it wasn't
- working right. It's still not as simple as I'd like, but it's better
- than before. Also added attributes to make more compatible with Apple's
- popup CDEF (such as using the window font to display the menu).
-
- 931224 aih
- - Converted to use handles.
- - Removed dependence on all other libraries, so that this library
- is completely self contained; the few external functions that were
- necessary (e.g., strcpy, TruncatePString) were copied or coded directly into
- this file. This will make this library more suitable for use as a
- CDEF since the linker won't pull in a lot of extra code from other
- libraries.
-
- 931223 aih
- - updated for current version of libraries
- - added port parameter to PopupBegin
-
- 911111 aih
- - Removed useless "PopupObj..." stuff
-
- 910301-05 aih
- - Update events are handled
- - Added comment describing this file
- - The popup's title is allocated as a handle in the heap
- - The popup is allocated as a handle in the heap
- - Attribute values are only updated if nescessary
- - The function PopupDraw first draws the popup to an offscreen bitmap
- and then copies it to the popup's port. This eliminates flicker when
- the popup is used as a CDEF, since the Control Manager sends a draw
- message to a control whenever the control is clicked, even if no
- drawing is actually needed.
-
- 910105 aih
- - Inserted this standard header in all files
-
- 901215 Ari Halberstadt (aih)
- - Created this file */
-
- #include <Fonts.h>
- #include <GestaltEqu.h>
- #include <Icons.h>
- #include <LowMem.h>
- #include <Memory.h>
- #include <OSUtils.h>
- #include <Palettes.h>
- #include <QuickDraw.h>
- #include <Resources.h>
- #include <Script.h>
- #include <TextEdit.h>
- #include <TextUtils.h>
- #include <Traps.h>
- #include <ToolUtils.h>
- #include <Windows.h>
- #include "PopupLib.h"
-
- /*-------------------------------------------------------------------------*/
- /* conditional compilation */
- /*-------------------------------------------------------------------------*/
-
- /* Define as 1 to bevel the popup when the port's depth is at
- least kBevelDepth. */
- #define DRAW_BEVEL (0)
-
- /* Define as 1 to draw a shaded triangle when the bevel attribute is
- enabled. */
- #define DRAW_SHADED_TRIANGLE (1)
-
- /* To reduce flicker the popup is normally first drawn to an off-screen
- bitmap, then copied to the screen. To make debugging easier disable
- offscreen drawing. Then you can step through each drawing routine to
- ensure that it is drawing correctly. */
- #define DRAW_OFFSCREEN (1)
-
- /* Define DRAW_POPUP as 1 to enable drawing of the various parts of the
- the menu. You will normally want this to be enabled, but, while
- debugging, you may want to see only the outlines of the parts of
- the menu. To do so, you can set DRAW_POPUP to 0 and DRAW_RECTANGLES
- to 1. */
- #define DRAW_POPUP (1)
-
- /* Define DRAW_RECTANGLES as 1 to enable framing of the various rectangles
- of the menu. This is useful when debugging since it clearly displays the
- rectangles, which otherwise can be tricky to calculate. */
- #define DRAW_RECTANGLES (0)
-
- /* To test right-justified menus without using a right-justified
- script system, define FLUSH_RIGHT as 1. */
- #define FLUSH_RIGHT (0)
-
- /* To copy the port's background pattern to the offscreen graphics world,
- defined COPY_BACKPAT as 1. This may not work correctly, so it's disabled
- for now. You might want to enable this if the popup is used in a port
- with a non-white background pattern. */
- #define COPY_BACKPAT (0)
-
- /* Another useful debugging trick is to define the following as something
- greater than 1. This helps spot and fix off-by-one errors caused by
- not taking into account the size of the frame and drop shadow.
- A value of at least 1/4" (18 pixels) provides good visual feedback
- and allows using a ruler to get rough measurements of areas. */
- #define kFrameSize (1) /* width of frame around popup */
- #define kShadowSize (1) /* width of shadow around popup */
- #define kBevelSize (1) /* width of bevel for 3D effect */
-
- /*-------------------------------------------------------------------------*/
- /* constants */
- /*-------------------------------------------------------------------------*/
-
- #define kEllipses '…' /* character appended to long strings */
- #define kShadowOffset (3) /* amount to offset shadow from frame */
- #define kIconMarginV (2) /* vertical margin around icon */
- #define kTriangleMargin (5) /* margin around triangle */
- #define kTriangleHeight (5) /* height for type-in popups */
- #define kTitleMargin (5) /* margin around title */
- #define kTitleMarginBottom (1) /* margin below title */
-
- #define kBevelDepth (8) /* minimum port depth for beveling */
- #define kDisableDepth (2) /* minimum depth for using color (rather
- than a gray pattern) to disable items */
-
- /* flags returned for part colors */
- typedef enum {
- kPartColorNone = 0,
- kPartColorBack = 1<<0,
- kPartColorFore = 1<<1,
- kPartColorBackAndFore = kPartColorBack + kPartColorFore,
- kPartColorLast
- } PartColorFlag;
-
- /* special values for the command key field of a menu item */
- enum {
- kCmdHier = hMenuCmd,
- kCmdScript,
- kCmdIconReduced,
- kCmdIconSmall,
- kCmdReserved
- };
-
- /*-------------------------------------------------------------------------*/
- /* types */
- /*-------------------------------------------------------------------------*/
-
- /* Private part codes. */
- typedef enum {
- kPopupPartFirst = 0, /* first part */
- kPopupPartBlackAndWhite = 0,/* always black and white */
- kPopupPartTitle, /* title rectangle */
- kPopupPartItem, /* current item rectangle */
- kPopupPartIcon, /* current item icon */
- kPopupPartTriangle, /* triangle */
- kPopupPartFrame, /* frame around current item */
- kPopupPartShadow, /* drop shadow */
- kPopupPartDefault, /* default color */
- kPopupPartLast
- } PopupPartType;
-
- /* for saving/restoring a port's text state */
- typedef struct {
- short font;
- Style face;
- short mode;
- short size;
- } TextState;
-
- /* for saving/restoring a port's color state */
- typedef struct {
- RGBColor fore;
- RGBColor back;
- } ColorState;
-
- /* structure for saving the drawing environment when drawing the current
- selection */
- typedef struct {
- TextState textState; /* saved text style */
- ColorState colorState; /* saved color state */
- } PopupDrawSelectionStructure;
-
- /* Drawing environment data that must be remembered before any drawing
- of the popup menu is begun and restored after all drawing has finished. */
- typedef struct {
- GrafPtr port; /* graf port on entry to popup cdef */
- PenState pen; /* saved pen state for popup's port */
- TextState text; /* saved text state for popup's port */
- ColorState color; /* saved color state for popup's port */
- RgnHandle clip; /* saved clip region for popup's port */
- short sysfont; /* saved system font */
- short syssize; /* saved system font size */
- short wmgrfont; /* saved window manager port font */
- short wmgrsize; /* saved window manager port font size */
- Boolean inserted; /* true if inserted menu into menu list */
- } PopupDrawStateType;
-
- /* used for translating a part code into a color part code */
- typedef struct {
- short fore;
- short back;
- short disabled;
- } PopupPartTableType;
-
- /*-------------------------------------------------------------------------*/
- /* assertions */
- /*-------------------------------------------------------------------------*/
-
- #if ASSERTS_DONT_WORK_TOO_WELL && DEBUG_CDEF
-
- #define require(x) ((void) ((x) || PopupAssertFailed()))
- #define ensure(x) ((void) ((x) || PopupAssertFailed()))
- #define check(x) ((void) ((x) || PopupAssertFailed()))
-
- static int PopupAssertFailed(void)
- {
- DebugStr((StringPtr) "\pPopupAssertFailed");
- return(0);
- }
-
- static Boolean RectValid(const Rect *r)
- {
- if (r->top > r->bottom) return(false);
- if (r->left > r->right) return(false);
- return(true);
- }
-
- static short RectWidth(const Rect *r)
- {
- return(r->right - r->left);
- }
-
- static short RectHeight(const Rect *r)
- {
- return(r->bottom - r->top);
- }
-
- static Boolean RectWithin(const Rect *r1, const Rect *r2)
- {
- return( r1->left >= r2->left && r1->right <= r2->right &&
- r1->top >= r2->top && r1->bottom <= r2->bottom);
- }
-
- #else /* DEBUG_CDEF */
-
- #define require(x) ((void) 0)
- #define ensure(x) ((void) 0)
- #define check(x) ((void) 0)
-
- #endif /* DEBUG_CDEF */
-
- /*-------------------------------------------------------------------------*/
- /* math utilities */
- /*-------------------------------------------------------------------------*/
-
- /* return minimum of two signed long integers */
- static long lmin(long a, long b)
- {
- return(a < b ? a : b);
- }
-
- /* return maximum of two signed long integers */
- static long lmax(long a, long b)
- {
- return(a > b ? a : b);
- }
-
- /*-------------------------------------------------------------------------*/
- /* trap utilities */
- /*-------------------------------------------------------------------------*/
-
- /* Functions for determining whether a trap is available. Based on
- functions given in IM VI. */
-
- /* TrapToolboxCount returns the number of ToolBox traps. */
- static short TrapToolboxCount(void)
- {
- short result = 0;
-
- if (NGetTrapAddress(_InitGraf, ToolTrap) == NGetTrapAddress(0xAA6E, ToolTrap))
- result = 0x0200;
- else
- result = 0x0400;
- return(result);
- }
-
- /* TrapType returns the type of the trap (ToolBox or Operating System). */
- static TrapType GetTrapType(short trap)
- {
- return((trap & 0x0800) > 0 ? ToolTrap : OSTrap);
- }
-
- /* TrapAvailable returns true if the trap is implemented. */
- static Boolean TrapAvailable(short trap)
- {
- TrapType type;
-
- type = GetTrapType(trap);
- if (type == ToolTrap) {
- trap &= 0x07FF;
- if (trap >= TrapToolboxCount())
- trap = _Unimplemented;
- }
- return(NGetTrapAddress(trap, type) != NGetTrapAddress(_Unimplemented, ToolTrap));
- }
-
- /*-------------------------------------------------------------------------*/
- /* gestalt utilities */
- /*-------------------------------------------------------------------------*/
-
- /* return the version of the Macintosh system software */
- static short MacVersion(void)
- {
- OSErr err;
- long response;
-
- err = Gestalt(gestaltSystemVersion, &response);
- return(! err ? LoWord(response) : 0);
- }
-
- /* return the version of QuickDraw */
- static short MacVersionQD(void)
- {
- OSErr err;
- long response;
-
- err = Gestalt(gestaltQuickdrawVersion, &response);
- return(! err ? LoWord(response) : 0);
- }
-
- /* true if the Macintosh supports color QuickDraw */
- static Boolean MacHasColorQD(void)
- {
- return(MacVersionQD() >= gestalt8BitQD);
- }
-
- /* true if color QuickDraw supports grayishTextOr */
- static Boolean MacHasGrayishTextOr(void)
- {
- OSErr err;
- long response;
-
- err = Gestalt(gestaltQuickdrawFeatures, &response);
- return(! err ? (response & gestaltHasGrayishTextOr) != 0 : false);
- }
-
- /* true if the Macintosh supports offscreen graphics worlds */
- static Boolean MacHasGWorlds(void)
- {
- /* GWorlds are supported if (MacVersionQD() >= gestalt32BitQD) but
- they behave sufficiently differently in systems before 7.0 that
- it's difficult to get them to work with both pre-7.0 and post-7.0
- systems, so we just support GWorlds in post-7.0 systems. */
- return(MacVersion() >= 0x0700);
- }
-
- /* true if the Macintosh has the icon utilities */
- static Boolean MacHasIconUtilities(void)
- {
- /* Develop, Issue 19, page 105 */
- return(TrapAvailable(_IconDispatch));
- }
-
- /*-------------------------------------------------------------------------*/
- /* glue utilities for patching mdef */
- /*-------------------------------------------------------------------------*/
-
- /* Initializes a glue structure for 68k or PowerPC. You must call this
- function before using the 'proc' function, at least once every time the
- CDEF is called. This will account for the possibility that the routine
- to be called has been moved in memory, or that the popup's data were
- last used by a version of the CDEF running in a different ISA. In
- other words, you must call this function whenever the address of the
- function in the 'proc' parameter may have changed and whenever the
- ISA of the code being executed may have changed.
- This function won't move or purge memory. */
- void PopupGlueInit(PopupGlueUnion *glue, ProcPtr proc, ProcInfoType info)
- {
- #if GENERATINGCFM
- /* initialize routine descriptor */
- RoutineDescriptor descriptor = BUILD_ROUTINE_DESCRIPTOR(0, 0);
- glue->ppc.descriptor = descriptor;
- glue->ppc.descriptor.routineRecords[0].procInfo = info;
- glue->ppc.descriptor.routineRecords[0].procDescriptor = proc;
- #else
- /* initialize 68K glue code */
- #pragma unused (info)
- glue->m68k.lea = 0x41fa0006; /* lea pc+8, a0 */
- glue->m68k.move = 0x2050; /* movea.l (a0) a0 */
- glue->m68k.jmp = 0x4ed0; /* jmp (a0) */
- glue->m68k.proc = proc; /* destination address */
- #endif
- }
-
- /*-------------------------------------------------------------------------*/
- /* graphics utilities */
- /*-------------------------------------------------------------------------*/
-
- /* true if the port is a color port */
- static Boolean PortIsColor(GrafPtr port)
- {
- return((((CGrafPtr) port)->portVersion & 0xC000) == 0xC000);
- }
-
- /* return the port's background pixel pattern */
- static PixPatHandle GetPortBackPixPat(GrafPtr port)
- {
- return(PortIsColor(port) ? ((CGrafPtr) port)->bkPixPat : NULL);
- }
-
- /* return the pixel depth of the pixel map */
- static short GetPixMapDepth(PixMapHandle pixmap)
- {
- return((**pixmap).pixelSize);
- }
-
- /* return the pixel depth of the port */
- static short GetPortDepth(GrafPtr port)
- {
- short depth;
-
- depth = 1;
- if (PortIsColor(port))
- depth = GetPixMapDepth(((CGrafPtr) port)->portPixMap);
- return(depth);
- }
-
- /* return the pixel depth of the graphics device */
- static short GetDeviceDepth(GDHandle device)
- {
- return(GetPixMapDepth((**device).gdPMap));
- }
-
- /* Return a handle to the video device with the smallest pixel depth
- that intersects the specified rectangle (given in global coordinates).
- If no video device intersects the specified rectangle, then NULL is
- returned. This is analogous to GetMaxDevice. */
- static GDHandle GetMinDevice(const Rect *globalRect)
- {
- GDHandle mindevice;
- GDHandle curdevice;
- Rect deviceRect;
-
- mindevice = NULL;
- curdevice = GetDeviceList();
- while (curdevice) {
- if (TestDeviceAttribute(curdevice, screenDevice) &&
- TestDeviceAttribute(curdevice, screenActive))
- {
- deviceRect = (**curdevice).gdRect;
- if (SectRect(&deviceRect, globalRect, &deviceRect)) {
- if (! mindevice ||
- GetDeviceDepth(curdevice) <
- GetDeviceDepth(mindevice))
- {
- mindevice = curdevice;
- }
- }
- }
- curdevice = GetNextDevice(curdevice);
- }
- return(mindevice);
- }
-
- /* return the pixel map of the graphics world */
- static PixMapHandle GetPixMap(GWorldPtr gworld)
- {
- return(MacVersion() >= 0x0700 ? GetGWorldPixMap(gworld) : gworld->portPixMap);
- }
-
- /* get the text attributes of the current port */
- static void GetTextState(TextState *text)
- {
- GrafPtr port;
-
- GetPort(&port);
- text->font = port->txFont;
- text->face = port->txFace;
- text->mode = port->txMode;
- text->size = port->txSize;
- }
-
- /* set the text attributes of the current port */
- static void SetTextState(const TextState *text)
- {
- TextFont(text->font);
- TextFace(text->face);
- TextMode(text->mode);
- TextSize(text->size);
- }
-
- /* set the text attributes of the current port to their defaults */
- static void TextNormal(void)
- {
- TextFont(0);
- TextFace(0);
- TextSize(0);
- TextMode(srcOr);
- }
-
- /* get the color attributes of the current port */
- static void GetColorState(ColorState *color)
- {
- if (MacHasColorQD()) {
- GetForeColor(&color->fore);
- GetBackColor(&color->back);
- }
- }
-
- /* set the color attributes of the current port */
- static void SetColorState(const ColorState *color)
- {
- if (MacHasColorQD()) {
- RGBForeColor(&color->fore);
- RGBBackColor(&color->back);
- }
- }
-
- /* true if the two colors are identical */
- static Boolean RGBColorEqual(const RGBColor *rgb1, const RGBColor *rgb2)
- {
- return( rgb1->red == rgb2->red &&
- rgb1->green == rgb2->green &&
- rgb1->blue == rgb2->blue);
- }
-
- /*-------------------------------------------------------------------------*/
- /* rectangle utilities */
- /*-------------------------------------------------------------------------*/
-
- /* Convert the rectangle to global coordinates. */
- static void RectLocalToGlobal(Rect *r)
- {
- Point origin = { 0, 0 };
-
- LocalToGlobal(&origin);
- OffsetRect(r, origin.h, origin.v);
- }
-
- /* Flip the rectangle 'flip' horizontally (mirror image) relative to
- the rectangle 'within'. Used for right-aligned popups. */
- static void RectFlip(Rect *flip, const Rect *within)
- {
- short offset;
-
- require(RectValid(flip));
- require(RectValid(within));
- require(within->left <= flip->left && flip->right <= within->right);
- offset = (within->right - flip->right) - (flip->left - within->left);
- flip->left += offset;
- flip->right += offset;
- }
-
- /* Paint over the rectangle with the gray pattern. Used for disabling
- text in black and white ports. */
- static void RectDisable(const Rect *r)
- {
- PenState pen;
- Pattern pat;
-
- require(RectValid(r));
- GetPenState(&pen);
- PenMode(patBic);
- GetIndPattern(&pat, sysPatListID, 4);
- PenPat(&pat);
- PaintRect(r);
- SetPenState(&pen);
- }
-
- /*-------------------------------------------------------------------------*/
- /* erasing rectangles and regions */
- /*-------------------------------------------------------------------------*/
-
- /* erase the rectangle using an RGB pattern (override bkPat) */
- static void RectEraseRGBPat(const Rect *r)
- {
- PixPatHandle backPat;
- RGBColor backColor;
- GrafPtr port;
-
- GetPort(&port);
- if (GetPortDepth(port) > 1) {
- GetBackColor(&backColor);
- backPat = NewPixPat();
- MakeRGBPat(backPat, &backColor);
- FillCRect(r, backPat);
- DisposePixPat(backPat);
- }
- else
- EraseRect(r);
- }
-
- /* erase the region an RGB pattern (override bkPat) */
- static void RgnEraseRGBPat(RgnHandle r)
- {
- PixPatHandle backPat;
- RGBColor backColor;
- GrafPtr port;
-
- GetPort(&port);
- if (GetPortDepth(port) > 1) {
- GetBackColor(&backColor);
- backPat = NewPixPat();
- MakeRGBPat(backPat, &backColor);
- FillCRgn(r, backPat);
- DisposePixPat(backPat);
- }
- else
- EraseRgn(r);
- }
-
- /* erase the rectangle */
- static void RectErase(const Rect *r, Boolean useBackColor)
- {
- if (useBackColor)
- RectEraseRGBPat(r);
- else
- EraseRect(r);
- }
-
- /* erase the region */
- static void RgnErase(RgnHandle r, Boolean useBackColor)
- {
- if (useBackColor)
- RgnEraseRGBPat(r);
- else
- EraseRgn(r);
- }
-
- /*-------------------------------------------------------------------------*/
- /* icon utilities */
- /*-------------------------------------------------------------------------*/
-
- /* PlotSICN draws a small black and white icon from a resource of type
- 'SICN' that defines a 16 by 16 bit image, in the specified rectangle.
- This is similar to the ToolBox routine PlotIcon. */
- static void PlotSICN(const Rect *bounds, Handle sicn)
- {
- SignedByte state;
- BitMap sicnBits;
- GrafPtr port;
-
- state = HGetState(sicn);
- MoveHHi(sicn);
- HLock(sicn);
- GetPort(&port);
- sicnBits.rowBytes = 2;
- sicnBits.baseAddr = *sicn;
- sicnBits.bounds = *bounds;
- CopyBits(&sicnBits, &port->portBits, bounds, bounds, srcCopy, NULL);
- HSetState(sicn, state);
- }
-
- /*-------------------------------------------------------------------------*/
- /* string utilities */
- /*-------------------------------------------------------------------------*/
-
- /* Clip the string to a maximum width by truncating it and adding an
- extra character and return final width of string. This is analogous
- to TruncString, but it works with both systems 6 and 7. */
- static short TruncatePString(Str255 str, short maxwidth)
- {
- const char extra = kEllipses;
- short width;
-
- require(maxwidth >= 0);
- if (MacVersion() >= 0x0700) {
- (void) TruncString(maxwidth, str, truncEnd);
- width = StringWidth(str);
- }
- else {
- width = StringWidth(str);
- if (width > maxwidth) {
- if (maxwidth == 0) {
- /* optimization */
- width = 0;
- *str = 0;
- }
- else {
- /* truncate one character (or multi-byte character)
- at a time from the end of the string */
- while (*str > 0) {
- str[*str] = extra;
- width = StringWidth(str);
- if (width <= maxwidth)
- break;
- while (CharByte((Ptr) str, *str) > 0) {
- check(*str > 1);
- --*str;
- }
- check(*str > 0);
- --*str;
- }
- }
- }
- }
- ensure(width <= maxwidth);
- ensure(width == StringWidth(str));
- return(width);
- }
-
- /*-------------------------------------------------------------------------*/
- /* menu utilities */
- /*-------------------------------------------------------------------------*/
-
- /* true if the menu item is enabled */
- static Boolean MenuItemEnabled(MenuHandle menu, short item)
- {
- unsigned long flags;
-
- require(0 <= item && item <= CountMItems(menu));
- flags = (**menu).enableFlags;
- return((flags & 1) != 0 && (item > 31 || (flags & (1UL << item)) != 0));
- }
-
- /* Get information about the icon for the specified menu item. */
- static void MenuItemIcon(MenuHandle menu, short item,
- ResType *iconType, short *iconID, Point *iconSize)
- {
- short itemCmd;
- short itemIcon;
- Rect iconRect;
- Handle iconRsrc;
-
- *iconID = 0;
- *iconType = 0;
- iconSize->h = 0;
- iconSize->v = 0;
- if (item > 0) {
- GetItemCmd(menu, item, &itemCmd);
- GetItemIcon(menu, item, &itemIcon);
- if (itemIcon && itemCmd != kCmdScript) {
- *iconID = itemIcon + 256;
- if (MacHasColorQD() && GetResource('cicn', *iconID)) {
- *iconType = 'cicn';
- iconRsrc = GetResource('cicn', *iconID);
- if (iconRsrc && *iconRsrc) {
- /* get icon's size from its pixmap */
- iconRect = (**(CIconHandle) iconRsrc).iconPMap.bounds;
- iconSize->h = iconRect.right - iconRect.left;
- iconSize->v = iconRect.bottom - iconRect.top;
- }
- }
- else if (itemCmd == kCmdIconSmall) {
- *iconType = 'SICN';
- iconSize->h = iconSize->v = 16;
- }
- else {
- *iconType = 'ICON';
- iconSize->h = iconSize->v = 32;
- }
- if (itemCmd == kCmdIconReduced) {
- /* reduce the size of the icon */
- iconSize->h /= 2;
- iconSize->v /= 2;
- }
- }
- }
- }
-
- /* If the menu item has a color associated with it, this sets the
- 'foreColor' and 'backColor' parameters to the menu item's colors
- and returns true. Otherwise, the 'foreColor' and 'backColor'
- parameters are not changed and the function returns false. */
- static Boolean MenuItemColor(MenuHandle menu, short item,
- RGBColor *foreColor, RGBColor *backColor)
- {
- MCEntryPtr mcentry; /* pointer to menu item's color entry */
- Boolean color; /* true if the item has a color */
-
- color = false;
- mcentry = GetMCEntry((**menu).menuID, item);
- if (item) {
- if (mcentry) {
- /* use item's color entry */
- if (foreColor) *foreColor = mcentry->mctRGB2;
- if (backColor) *backColor = mcentry->mctRGB4;
- color = true;
- }
- else {
- /* use default color entry */
- mcentry = GetMCEntry((**menu).menuID, 0);
- if (mcentry) {
- if (foreColor) *foreColor = mcentry->mctRGB3;
- if (backColor) *backColor = mcentry->mctRGB4;
- color = true;
- }
- }
- }
- else if (mcentry) {
- /* use title's color entry */
- if (foreColor) *foreColor = mcentry->mctRGB1;
- if (backColor) *backColor = mcentry->mctRGB4;
- color = true;
- }
- return(color);
- }
-
- /* This is similar to MenuItemColor, but returns the colors from the
- color table for the menu bar. */
- static Boolean MenuItemColorDefault(short item,
- RGBColor *foreColor, RGBColor *backColor)
- {
- MCEntryPtr mcentry; /* pointer to menu item's color entry */
- Boolean color; /* true if the item has a color */
-
- color = false;
- mcentry = GetMCEntry(0, 0);
- if (mcentry) {
- color = true;
- if (item > 0) {
- /* use item entry */
- if (foreColor) *foreColor = mcentry->mctRGB3;
- if (backColor) *backColor = mcentry->mctRGB2;
- }
- else {
- /* use title entry */
- if (foreColor) *foreColor = mcentry->mctRGB1;
- if (backColor) *backColor = mcentry->mctRGB4;
- }
- }
- return(color);
- }
-
- /*-------------------------------------------------------------------------*/
- /* control utilities */
- /*-------------------------------------------------------------------------*/
-
- /* Return the control's color table, or NULL if it doesn't have
- a color table. */
- static CCTabHandle ControlColorTable(ControlHandle ctl)
- {
- AuxCtlHandle aux;
-
- return(GetAuxiliaryControlRecord(ctl, &aux) ? (**aux).acCTable : NULL);
- }
-
- /* If the part has an entry in the control's color table, then this
- sets the 'color' parameter to the color specified in the table
- and returns true, otherwise the 'color' parameter is not changed
- and the function returns false. */
- static Boolean ControlCTPartColor(CCTabHandle cct, short part,
- RGBColor *color)
- {
- Boolean found;
- short i;
-
- found = false;
- if (cct) {
- for (i = 0; i <= (**cct).ctSize && ! found; i++) {
- if ((**cct).ctTable[i].value == part) {
- if (color)
- *color = (**cct).ctTable[i].rgb;
- found = true;
- }
- }
- }
- return(found);
- }
-
- /*-------------------------------------------------------------------------*/
- /* popup menu routines */
- /*-------------------------------------------------------------------------*/
-
- /* Returns true if the popup handle is valid. */
- Boolean PopupValid(PopupHandle popup)
- {
- if (! popup) return(false);
- if (GetHandleSize((Handle) popup) < sizeof(PopupType)) return(false);
- if ((**popup).version != kPopupVersion) return(false);
- return(true);
- }
-
- /*-------------------------------------------------------------------------*/
- /* port setup and restore */
- /*-------------------------------------------------------------------------*/
-
- /* Inserts the menu into the hierarchical menu list if necessary.
- This also sets the menu's color table if there is a 'mctb'
- resource with the same ID as the menu. True is returned if the
- menu was inserted into the menu table, in which case
- PopupDeleteMenu should be called when finished using the menu. */
- static Boolean PopupInsertMenu(PopupHandle popup)
- {
- Boolean inserted;
- SignedByte state;
- Handle mctb;
-
- require(PopupValid(popup));
- inserted = false;
- if (! GetMenuHandle((**(**popup).menu).menuID)) {
- inserted = true;
- InsertMenu((**popup).menu, hierMenu);
- if (MacHasColorQD()) {
- mctb = GetResource('mctb', (**(**popup).menu).menuID);
- if (mctb && *mctb) {
- state = HGetState(mctb);
- MoveHHi(mctb);
- HLock(mctb);
- SetMCEntries(*(short *) *mctb, (MCTablePtr) (*mctb + sizeof(short)));
- HSetState(mctb, state);
- }
- }
- }
- return(inserted);
- }
-
- /* Remove the menu from the menu list. */
- static void PopupDeleteMenu(PopupHandle popup, Boolean inserted)
- {
- require(PopupValid(popup));
- if (inserted)
- DeleteMenu((**(**popup).menu).menuID);
- }
-
- /* Return the alignment for the font in which the popup menu is drawn. */
- static short PopupFontJust(PopupHandle popup)
- {
- GrafPtr savePort;
- short just;
-
- #if FLUSH_RIGHT
- just = teFlushRight;
- #else /* FLUSH_RIGHT */
- if ((**popup).attr.windowFont) {
- GetPort(&savePort);
- SetPort((**popup).port);
- just = GetScriptVariable(FontScript(), smScriptJust);
- SetPort(savePort);
- }
- else
- just = GetSysDirection();
- #endif /* FLUSH_RIGHT */
- return(just);
- }
-
- /* Return the pixel depth of the port or device to which the popup is
- being drawn. */
- static short PopupPixelDepth(PopupHandle popup)
- {
- short depth;
-
- depth = GetPortDepth((**popup).port);
- if ((**popup).draw.gdepth)
- depth = lmin(depth, (**popup).draw.gdepth);
- return(depth);
- }
-
- /* Return true if the popup is drawn into a color port. */
- static Boolean PopupPortIsColor(PopupHandle popup)
- {
- return(PopupPixelDepth(popup) > 1);
- }
-
- /* Remember the current drawing environment of the system font and
- size and sets the system font and size to the popup font and size.
- Must be balanced with a call to PopupPortRestoreSystem. */
- static void PopupPortSetupSystem(PopupHandle popup, PopupDrawStateType *state)
- {
- GrafPtr svport;
- GrafPtr wmgrPort;
-
- if ((**popup).attr.windowFont) {
-
- /* force rebuild of font cache so MDEF will use the new fonts */
- FlushFonts();
-
- /* If using the window's font, then set the system font and size
- to the font and size of the popup's port. */
- state->sysfont = LMGetSysFontFam();
- state->syssize = LMGetSysFontSize();
- LMSetSysFontFam((**popup).port->txFont);
- LMSetSysFontSize((**popup).port->txSize);
-
- /* The menu definition function gets the font size in which to draw the
- popup menu from the font size of the window manager port. So, to
- set the system font, in addition to setting a low-memory global,
- we also have to set the font size for the window manager port. */
- GetPort(&svport);
- GetWMgrPort(&wmgrPort);
- SetPort(wmgrPort);
- state->wmgrfont = wmgrPort->txFont;
- state->wmgrsize = wmgrPort->txFont;
- TextFont((**popup).port->txFont);
- TextSize((**popup).port->txSize);
- SetPort(svport);
- }
- }
-
- /* Restore the current drawing environment of the system font and size. */
- static void PopupPortRestoreSystem(PopupHandle popup, const PopupDrawStateType *state)
- {
- GrafPtr svport;
- GrafPtr wmgrPort;
-
- if ((**popup).attr.windowFont) {
- LMSetSysFontFam(state->sysfont);
- LMSetSysFontSize(state->syssize);
- GetPort(&svport);
- GetWMgrPort(&wmgrPort);
- SetPort(wmgrPort);
- TextFont(state->wmgrfont);
- TextSize(state->wmgrsize);
- SetPort(svport);
- }
- }
-
- /* Remember the current drawing environment and set the drawing environment
- to that needed by the popup menu. Must be balanced with a call
- to PopupPortRestore. */
- static void PopupPortSetup(PopupHandle popup, PopupDrawStateType *state)
- {
- require(! (**popup).state.drawSetup);
-
- #if FLUSH_RIGHT
- SetSysDirection(teFlushRight);
- #endif
-
- /* increment setup counter so we know when this is the first setup call */
- (**popup).state.drawSetup++;
-
- /* remember current port */
- GetPort(&state->port);
-
- /* save settings for popup's port */
- SetPort((**popup).port);
- GetPenState(&state->pen);
- GetTextState(&state->text);
- GetColorState(&state->color);
-
- /* remember popup port's default colors */
- if ((**popup).state.drawSetup == 1) {
- (**popup).draw.fore = state->color.fore;
- (**popup).draw.back = state->color.back;
- }
-
- /* switch to offscreen graphics world */
- if ((**popup).draw.gworld && state->port == (GrafPtr) (**popup).draw.gworld)
- SetGWorld((**popup).draw.gworld, NULL);
-
- /* reset text and pen settings to default system settings */
- TextNormal();
- PenNormal();
-
- /* if using the window's font, use font and font size of popup's port */
- if ((**popup).attr.windowFont) {
- TextFont(state->text.font);
- TextSize(state->text.size);
- }
-
- /* save clip region and clip to the popup's maximum bounding
- rectangle intersected with old clip region */
- state->clip = NewRgn();
- if (state->clip) {
- Rect maxbounds = (**popup).maxbounds;
- RgnHandle boundsClip = NewRgn();
- check(boundsClip != NULL);
- RectRgn(boundsClip, &maxbounds);
- GetClip(state->clip);
- SectRgn(state->clip, boundsClip, boundsClip);
- SetClip(boundsClip);
- DisposeRgn(boundsClip);
- }
-
- /* insert menu into menu list so we can access its color table */
- state->inserted = PopupInsertMenu(popup);
-
- /* setup system port */
- PopupPortSetupSystem(popup, state);
-
- ensure((**popup).state.drawSetup > 0);
- }
-
- /* Restore the drawing environment to its original state. */
- static void PopupPortRestore(PopupHandle popup, PopupDrawStateType *state)
- {
- require((**popup).state.drawSetup > 0);
- PopupPortRestoreSystem(popup, state);
- PopupDeleteMenu(popup, state->inserted);
- if (state->clip) {
- SetClip(state->clip);
- DisposeRgn(state->clip);
- state->clip = NULL;
- }
- SetColorState(&state->color);
- SetTextState(&state->text);
- SetPenState(&state->pen);
- SetPort(state->port);
- #if FLUSH_RIGHT
- SetSysDirection(0);
- #endif
- (**popup).state.drawSetup--;
- ensure((**popup).state.drawSetup >= 0);
- }
-
- /* Setup the drawing environment for drawing the current selection string. */
- static void PopupPortSelectionSetup(PopupHandle popup,
- PopupDrawSelectionStructure *save)
- {
- Style itemStyle; /* menu item's style */
- short itemCmd; /* item's command character */
- short itemScript; /* item's script code */
- long itemFondSize; /* item's font ID and size */
-
- /* save the text and color states */
- GetTextState(&save->textState);
- GetColorState(&save->colorState);
-
- /* get current item's font and style */
- if ((**popup).state.current > 0) {
-
- /* set the font script of the menu item */
- GetItemCmd((**popup).menu, (**popup).state.current, &itemCmd);
- if (itemCmd == kCmdScript) {
- GetItemIcon((**popup).menu, (**popup).state.current, &itemScript);
- if (GetScriptVariable(itemScript, smScriptEnabled)) {
- itemFondSize = GetScriptVariable(itemScript, smScriptSysFondSize);
- TextFont(HiWord(itemFondSize));
- TextSize(LoWord(itemFondSize));
- }
- }
-
- /* set the text style of the menu item */
- GetItemStyle((**popup).menu, (**popup).state.current, &itemStyle);
- TextFace(itemStyle);
- }
- }
-
- /* restore the drawing environment after drawing the current selection */
- static void PopupPortSelectionRestore(PopupHandle popup,
- const PopupDrawSelectionStructure *save)
- {
- #pragma unused(popup)
- SetTextState(&save->textState);
- SetColorState(&save->colorState);
- }
-
- /*-------------------------------------------------------------------------*/
- /* rectangle calculation routines */
- /*-------------------------------------------------------------------------*/
-
- /* set popup's rectangles and erase old popup if the frame has changed */
- static void PopupRectanglesSet(PopupHandle popup, const PopupRectanglesType *r)
- {
- Rect bounds;
- RgnHandle eraseRgn;
- RgnHandle boundsRgn;
-
- bounds = (**popup).r.bounds;
- if (! EqualRect(&bounds, &r->bounds)) {
- eraseRgn = NewRgn();
- boundsRgn = NewRgn();
- if (eraseRgn && boundsRgn) {
- /* to reduce flicker only erase the
- area that doesn't overlap */
- RectRgn(eraseRgn, &bounds);
- RectRgn(boundsRgn, &r->bounds);
- DiffRgn(eraseRgn, boundsRgn, eraseRgn);
- EraseRgn(eraseRgn);
- DisposeRgn(eraseRgn);
- DisposeRgn(boundsRgn);
- }
- else
- RectErase(&bounds, false);
- }
- (**popup).r = *r;
- }
-
- /* calculate the popup's rectangles; this is an ugly function
- which I'd like to redo */
- void PopupCalculate(PopupHandle popup)
- {
- struct { /* margins around some items */
- struct { short left, right; } selection;
- struct { short left, right; } title;
- struct { short left, right; } triangle;
- } margin = {
- { kFrameSize, 0 },
- { kTitleMargin, kTitleMargin },
- { 0, kTriangleMargin },
- };
- struct { /* minimum widths of each item */
- short content;
- short hilite;
- short title;
- short triangle;
- short selection;
- } minwidth;
- short lineHeight; /* height of a line */
- short menuHeight; /* height of the selected menu item */
- short menuWidth; /* width of the menu */
- short triangleHeight; /* height of triangle */
- short triangleWidth; /* width of triangle */
- short maxwidth; /* for calculating maximum widths of items */
- short width; /* for calculating widths of items */
- Point iconSize; /* size of current item's icon */
- Rect maxbounds; /* popup's maximum bounding rectangle */
- ResType iconType; /* resource type of current item's icon */
- short iconID; /* resource ID of current item's icon */
- FontInfo font; /* info about the current font size */
- Str255 title; /* the popup's title */
- Rect content; /* rectangle enclosing content (excludes frame) */
- GrafPtr port; /* the port the popup is drawn into */
- PopupRectanglesType r; /* the calculated rectangles */
- PopupDrawSelectionStructure saveState; /* saved state for calculating menu height */
- PopupDrawStateType saveDrawState; /* save port settings */
-
- /* don't bother calculating if drawing is turned off */
- if (! (**popup).attr.draw) return;
-
- /* initialize settings */
- PopupPortSetup(popup, &saveDrawState);
- PopupTitle(popup, title);
- GetPort(&port);
-
- /* get width of menu, except for the width of the triangle */
- CalcMenuSize((**popup).menu);
- menuWidth = (**(**popup).menu).menuWidth;
-
- /* setup port for current selection string */
- PopupPortSelectionSetup(popup, &saveState);
-
- /* get information about the font in which the current selection
- will be drawn */
- GetFontInfo(&font);
- lineHeight = font.ascent + font.descent + font.leading;
- check(lineHeight > 0);
-
- /* calculate size of triangle */
- if ((**popup).attr.typeIn)
- triangleHeight = kTriangleHeight;
- else
- triangleHeight = lineHeight / 3;
- triangleWidth = triangleHeight * 2;
- check(triangleWidth % 2 == 0);
-
- /* add the width of the triangle to the menu's width */
- menuWidth += triangleWidth + kTriangleMargin;
-
- /* calculate height of current selection */
- menuHeight = 0;
- MenuItemIcon((**popup).menu, (**popup).state.current,
- &iconType, &iconID, &iconSize);
- if (iconSize.v > 0)
- menuHeight = iconSize.v + kIconMarginV;
-
- /* the height must be at least as large as the minimum height
- of a line and the minimum height needed to display the
- triangle */
- if (menuHeight < lineHeight)
- menuHeight = lineHeight;
- if (menuHeight < triangleHeight + 4)
- menuHeight = triangleHeight + 4;
-
- /* restore port */
- PopupPortSelectionRestore(popup, &saveState);
-
- /* get information about the font in which the title will be drawn */
- {
- Style style = port->txFace;
- TextFace((**popup).attr.title.style);
- GetFontInfo(&font);
- TextFace(style);
- }
- lineHeight = font.ascent + font.descent + font.leading;
-
- /* adjust margins */
-
- if (! *title || (**popup).attr.typeIn) {
- /* Type-in popups don't display the title or the current selection,
- so those parts' margins aren't needed. Also, if the title is empty
- then the title's margins can also be empty. */
- if ((**popup).attr.typeIn) {
- margin.triangle.left = 3;
- margin.triangle.right = 4;
- menuHeight = lineHeight;
- font.widMax = 0;
- }
- margin.title.left = 0;
- margin.title.right = 0;
- }
-
- /* for right justified popups we need a little extra space for the drop
- shadow between the current selection area and the title */
- if (PopupFontJust(popup) == teFlushRight)
- margin.selection.left += kShadowSize;
-
- /* calculate minimum widths */
-
- minwidth.triangle = triangleWidth;
- minwidth.title = (*title ? font.widMax : 0);
- minwidth.hilite = minwidth.title + margin.title.left + margin.title.right;
- minwidth.selection = font.widMax + minwidth.triangle + margin.triangle.left + margin.triangle.right;
- minwidth.content = minwidth.hilite + minwidth.selection +
- margin.selection.left + margin.selection.right;
-
- /* calculate rectangles */
-
- /* copy maxbounds rectangle */
- maxbounds = (**popup).maxbounds;
-
- /* calculate content rectangle; all the other rectangles will be
- calculated relative to this rectangle */
- content.top = maxbounds.top + kFrameSize;
- content.left = maxbounds.left + kFrameSize;
- content.bottom = content.top + menuHeight;
- content.right = content.left +
- lmax(maxbounds.right - maxbounds.left - 2 * kFrameSize - kShadowSize,
- minwidth.content);
-
- /* vertically center content in maxbounds */
- { short offset = ((maxbounds.bottom - maxbounds.top) -
- (content.bottom - content.top)) / 2;
- OffsetRect(&content, 0, lmax(0, offset - kShadowSize));
- }
-
- /* calculate hilite rectangle */
- r.hilite = content;
- r.hilite.left = content.left;
- r.hilite.right = content.right - minwidth.selection;
-
- /* calculate title rectangle */
- check(content.bottom - content.top >= lineHeight);
- r.title.top = content.top + (content.bottom - content.top - lineHeight) / 2;
- r.title.left = content.left + margin.title.left;
- r.title.bottom = r.title.top + lineHeight - kTitleMarginBottom;
- /* calculate width of title rectangle */
- if ((**popup).attr.typeIn || ! *title)
- width = 0; /* don't show title */
- else {
- /* popup has a title */
- maxwidth =
- r.hilite.right - r.hilite.left -
- margin.title.left - margin.title.right;
- check(maxwidth >= minwidth.title);
- if ((**popup).attr.title.width) {
- /* use fixed title width as specified by application */
- width = lmin(maxwidth, (**popup).attr.title.width);
- width = lmax(width, minwidth.title);
- }
- else {
- /* use actual width of title string */
- Style style = port->txFace;
- TextFace((**popup).attr.title.style);
- width = TruncatePString(title, maxwidth);
- TextFace(style);
- }
- check(width <= maxwidth);
- }
- check(width >= minwidth.title);
- r.title.right = r.title.left + width;
-
- /* adjust right edge of hilite rectangle now that width of title is known */
- r.hilite.right = r.title.right + margin.title.right;
-
- /* calculate selection rectangle, initially using the maximum possible width */
- r.selection = content;
- r.selection.left = r.hilite.right + margin.selection.left;
- r.selection.right = content.right - margin.selection.right;
-
- /* calculate width of current selection */
- if ((**popup).attr.typeIn)
- width = minwidth.selection;
- else if ((**popup).attr.fixedWidth)
- width = lmax(r.selection.right - r.selection.left, minwidth.selection);
- else {
- maxwidth = r.selection.right - r.selection.left;
- width = lmin(maxwidth, menuWidth);
- width = lmax(width, minwidth.selection);
- }
-
- /* adjust right edge now that we know how wide everything is */
- r.selection.right = r.selection.left + width;
- content.right = r.selection.right + margin.selection.right;
-
- /* calculate triangle rectangle--center triangle vertically at
- right edge of selection rectangle */
- check(content.bottom - content.top >= triangleHeight);
- r.triangle.top = content.top + (content.bottom - content.top - triangleHeight) / 2;
- r.triangle.left = r.selection.right - triangleWidth - margin.triangle.right;
- r.triangle.bottom = r.triangle.top + triangleHeight;
- r.triangle.right = r.triangle.left + triangleWidth;
-
- /* calculate frame rectangle--surrounds content area, and includes the
- frame and shadow */
- r.bounds.top = content.top - kFrameSize;
- r.bounds.left = content.left - kFrameSize;
- r.bounds.right = content.right + kFrameSize;
- r.bounds.bottom = content.bottom + kFrameSize + kShadowSize;
- if (PopupFontJust(popup) != teFlushRight)
- r.bounds.right += kShadowSize; /* in teFlushRight shadow is part of content */
-
- /* mirror image rectangles if using a right justified menu */
- if (PopupFontJust(popup) == teFlushRight) {
- Rect flipbounds;
-
- /* copy and adjust maxbounds so flipping will work */
- flipbounds.top = maxbounds.top;
- flipbounds.left = maxbounds.left;
- flipbounds.bottom = maxbounds.top +
- lmax(maxbounds.bottom - maxbounds.top,
- r.bounds.bottom - r.bounds.top);
- flipbounds.right = maxbounds.left +
- lmax(maxbounds.right - maxbounds.left,
- r.bounds.right - r.bounds.left);
-
- /* flip the rectangles */
- RectFlip(&r.bounds, &flipbounds);
- RectFlip(&r.hilite, &flipbounds);
- RectFlip(&r.selection, &flipbounds);
- RectFlip(&r.title, &flipbounds);
- RectFlip(&r.triangle, &flipbounds);
- if (PopupFontJust(popup) != teFlushRight)
- RectFlip(&r.triangle, &r.selection);
- }
-
- /* make sure everything's ok */
- check(RectWidth(&r.hilite) >= minwidth.hilite);
- check(RectWidth(&r.title) >= minwidth.title);
- check(RectWidth(&r.selection) >= minwidth.selection);
- check(RectWidth(&r.triangle) >= minwidth.triangle);
- check(RectWithin(&r.hilite, &r.bounds));
- check(RectWithin(&r.title, &r.hilite));
- check(RectWithin(&r.selection, &r.bounds));
- check(RectWithin(&r.triangle, &r.selection));
- check(! RectWithin(&r.hilite, &r.selection));
- check(! RectWithin(&r.selection, &r.hilite));
-
- /* set the popup's rectangles and restore the environment */
- PopupRectanglesSet(popup, &r);
- PopupPortRestore(popup, &saveDrawState);
- }
-
- /*-------------------------------------------------------------------------*/
- /* popup colors */
- /*-------------------------------------------------------------------------*/
-
- /* Set the 'foreColor' and 'backColor' parameters to a blend of those
- colors, adjusted for the current graphics device. */
- static void PopupGetGray(PopupHandle popup,
- RGBColor *backColor,
- RGBColor *foreColor,
- RGBColor *grayColor)
- {
- RGBColor tmpColor;
-
- tmpColor = *foreColor;
- if ((**popup).draw.gdevice)
- (void) GetGray((**popup).draw.gdevice, backColor, &tmpColor);
- else
- (void) GetGray(GetMainDevice(), backColor, &tmpColor);
- *grayColor = tmpColor;
- }
-
- /* Set the 'foreColor' and 'backColor' parameters to the foreground and
- background colors for the specified part of the control. The 'textMode'
- parameter is set to grayishTextOr if text should be grayed out,
- otherwise it is set to the text mode of the current port. The
- current port should be the port into which the popup is being
- drawn.
-
- Returns flags indicating if the part was found in the control
- or menu color tables (that is, color is not a default color
- and an entry for the color exists in the control or menu's
- color table).*/
- static PartColorFlag PopupPartColor(PopupHandle popup, short part,
- RGBColor *foreColor, RGBColor *backColor, short *textMode)
- {
- const RGBColor whiteRGBColor = { 65535, 65535, 65535 };
- const RGBColor blackRGBColor = { 0, 0, 0 };
- const PopupPartTableType table[kPopupPartLast - kPopupPartFirst] = {
- { kPopupPartBlackAndWhite, kPopupPartBlackAndWhite, kPopupPartBlackAndWhite, },
- { kPopupPartTitleFore, kPopupPartTitleBack, kPopupPartTitleDisabled, },
- { kPopupPartItemFore, kPopupPartItemBack, kPopupPartItemDisabled, },
- { kPopupPartIconFore, kPopupPartIconBack, kPopupPartIconDisabled, },
- { kPopupPartTriangleFore, kPopupPartTriangleBack, kPopupPartTriangleDisabled, },
- { kPopupPartFrameFore, kPopupPartFrameBack, kPopupPartFrameDisabled, },
- { kPopupPartShadowFore, kPopupPartShadowBack, kPopupPartShadowDisabled, },
- { kPopupPartDefaultFore, kPopupPartDefaultBack, kPopupPartDefaultDisabled, },
- };
- const PopupPartTableType *entry; /* entry in table */
- Boolean defaultFore; /* true if got a default foreground color from cctb */
- Boolean defaultBack; /* true if got a default background color from cctb */
- Boolean fore; /* true if got a foreground color from cctb */
- Boolean back; /* true if got a background color from cctb */
- Boolean gray; /* true if should select a gray color */
- Boolean enabled; /* true if the part is enabled */
- PartColorFlag found; /* flags if found a color entry */
- CCTabHandle cct; /* control's color table */
- CCTabHandle dcct; /* default control color table */
- short item; /* menu item to get color for */
-
- require((**popup).state.drawSetup);
- require(kPopupPartFirst <= part && part < kPopupPartLast);
-
- /* only use color if the destination port is a color port
- (otherwise, the colors would be mapped to black, which
- would be pretty ugly) */
- *textMode = (**popup).port->txMode;
- if (PopupPortIsColor(popup)) {
-
- check(MacHasColorQD());
-
- /* get control color tables */
- dcct = ControlColorTable(NULL);
- cct = ControlColorTable((**popup).ctl);
- if (cct == dcct)
- cct = NULL;
-
- /* find entry in part code table */
- entry = table + (part - kPopupPartFirst);
-
- /* determine if part is enabled */
- enabled = (**popup).attr.enabled;
- if (enabled) {
- switch (part) {
- case kPopupPartItem:
- case kPopupPartIcon:
- enabled = MenuItemEnabled((**popup).menu, (**popup).state.current);
- break;
- }
- }
-
- /* set gray mode */
- gray = false;
-
- /* get default colors from the control color table */
- *foreColor = blackRGBColor;
- *backColor = whiteRGBColor;
- defaultFore = ControlCTPartColor(cct, kPopupPartDefaultFore, foreColor);
- defaultBack = ControlCTPartColor(cct, kPopupPartDefaultBack, backColor);
- if (! defaultFore) {
- *foreColor = (**popup).draw.fore;
- defaultFore = ! RGBColorEqual(&blackRGBColor, foreColor);
- }
- if (! defaultBack) {
- *backColor = (**popup).draw.back;
- defaultBack = ! RGBColorEqual(&whiteRGBColor, backColor);
- }
-
- /* get color for part */
- found = kPartColorNone;
- fore = back = false;
- switch (part) {
- case kPopupPartBlackAndWhite:
- *foreColor = blackRGBColor;
- *backColor = whiteRGBColor;
- found = kPartColorBackAndFore;
- break;
- case kPopupPartDefault:
- break;
- case kPopupPartTitle:
- case kPopupPartItem:
- case kPopupPartIcon:
- switch (part) {
- case kPopupPartTitle:
- item = 0;
- break;
- case kPopupPartItem:
- item = (**popup).state.current;
- break;
- case kPopupPartIcon:
- item = (**popup).state.current;
- break;
- }
- if (MenuItemColor((**popup).menu, item, foreColor, backColor))
- found = kPartColorBackAndFore;
- if (found == kPartColorNone) {
- fore = ControlCTPartColor(cct, entry->fore, foreColor);
- back = ControlCTPartColor(cct, entry->back, backColor);
- if (fore) found |= kPartColorFore;
- if (back) found |= kPartColorBack;
- if (! fore && ! defaultFore) fore = MenuItemColorDefault(item, foreColor, NULL);
- if (! back && ! defaultBack) back = MenuItemColorDefault(item, NULL, backColor);
- if (! fore && ! defaultFore) (void) ControlCTPartColor(dcct, cTextColor, foreColor);
- if (! back && ! defaultBack) (void) ControlCTPartColor(dcct, cBodyColor, backColor);
- }
- if (! enabled) {
- if (ControlCTPartColor(cct, entry->disabled, foreColor))
- found |= kPartColorFore;
- else {
- gray = (part == kPopupPartIcon);
- if (MacHasGrayishTextOr())
- *textMode = grayishTextOr;
- else
- gray = true;
- }
- }
- break;
- case kPopupPartTriangle:
- case kPopupPartFrame:
- case kPopupPartShadow:
- fore = ControlCTPartColor(cct, entry->fore, foreColor);
- back = ControlCTPartColor(cct, entry->back, backColor);
- if (fore) found |= kPartColorFore;
- if (back) found |= kPartColorBack;
- switch (part) {
- case kPopupPartTriangle:
- if (! fore && ! defaultFore) (void) ControlCTPartColor(dcct, cTextColor, foreColor);
- if (! back && ! defaultBack) (void) ControlCTPartColor(dcct, cBodyColor, backColor);
- break;
- case kPopupPartShadow:
- case kPopupPartFrame:
- if (! fore && ! defaultFore) (void) ControlCTPartColor(dcct, cFrameColor, foreColor);
- break;
- }
- if (! enabled) {
- gray = ! ControlCTPartColor(cct, entry->disabled, foreColor);
- if (! gray)
- found |= kPartColorFore;
- }
- if ( (found & kPartColorFore) == kPartColorNone &&
- part == kPopupPartShadow &&
- (**popup).attr.bevel &&
- PopupPixelDepth(popup) >= kBevelDepth)
- {
- gray = true;
- }
- break;
- default:
- /* error */
- check(false);
- }
-
- /* derive a gray color from a blend of the foreground and background colors */
- if (gray)
- PopupGetGray(popup, backColor, foreColor, foreColor);
- }
- return(found);
- }
-
- /* Set the foreground and background colors to the colors appropriate for
- drawing the specified part of the control, or invert the colors if the
- 'invert' flag is true. Returns non-zero if the part was found in the control
- or menu color table (that is, the color is not a default color) and
- the popup is being drawn in a color port. */
- static PartColorFlag PopupPartColorInvertSet(PopupHandle popup, short part, Boolean invert)
- {
- PartColorFlag found;
- RGBColor foreColor;
- RGBColor backColor;
- short mode;
-
- found = kPartColorNone;
- if (PopupPortIsColor(popup)) {
- if (invert)
- found = PopupPartColor(popup, part, &backColor, &foreColor, &mode);
- else
- found = PopupPartColor(popup, part, &foreColor, &backColor, &mode);
- RGBForeColor(&foreColor);
- RGBBackColor(&backColor);
- TextMode(mode);
- }
- return(found);
- }
-
- /* See PopupPartColorInvertSet. */
- static PartColorFlag PopupPartColorSet(PopupHandle popup, short part)
- {
- return(PopupPartColorInvertSet(popup, part, false));
- }
-
- /* Calculate bevel colors. */
- static void PopupBevelColors(PopupHandle popup,
- RGBColor *bevelUpperColor,
- RGBColor *bevelLowerColor,
- RGBColor *bevelCornerColor)
- {
- RGBColor frameColor;
- RGBColor itemColor;
- RGBColor dummyColor;
- short dummyMode;
-
- (void) PopupPartColor(popup, kPopupPartFrame, &frameColor, &dummyColor, &dummyMode);
- (void) PopupPartColor(popup, kPopupPartItem, &dummyColor, &itemColor, &dummyMode);
- (void) PopupPartColor(popup, kPopupPartBlackAndWhite, &dummyColor, bevelUpperColor, &dummyMode);
- PopupGetGray(popup, &itemColor, &frameColor, bevelLowerColor);
- PopupGetGray(popup, bevelUpperColor, bevelLowerColor, bevelCornerColor);
- }
-
- /*-------------------------------------------------------------------------*/
- /* more drawing routines */
- /*-------------------------------------------------------------------------*/
-
- /* calculate frame rectangle */
- static void PopupFrameRectangle(PopupHandle popup, Rect *frame, Boolean shadow)
- {
- *frame = (**popup).r.bounds;
- if (PopupFontJust(popup) == teFlushRight)
- frame->right = (**popup).r.selection.right + kFrameSize + kShadowSize;
- else
- frame->left = (**popup).r.selection.left - kFrameSize;
- if (! shadow) {
- frame->right -= kShadowSize;
- frame->bottom -= kShadowSize;
- }
- }
-
- /* draw the bevel around the popup menu */
- static void PopupDrawBevel(PopupHandle popup, const Rect *bevel)
- {
- RGBColor bevelUpperColor;
- RGBColor bevelLowerColor;
- RGBColor bevelCornerColor;
-
- require((**popup).state.drawSetup);
- require(RectValid(bevel));
-
- /* get bevel colors */
- PopupBevelColors(popup, &bevelUpperColor, &bevelLowerColor, &bevelCornerColor);
- PopupGetGray(popup, &bevelUpperColor, &bevelLowerColor, &bevelLowerColor);
- PopupGetGray(popup, &bevelUpperColor, &bevelLowerColor, &bevelCornerColor);
-
- /* draw upper bevel */
- RGBForeColor(&bevelUpperColor);
- PenSize(kBevelSize, kBevelSize);
- MoveTo(bevel->left, bevel->bottom - kBevelSize - kShadowSize);
- LineTo(bevel->left, bevel->top);
- LineTo(bevel->right - kBevelSize - kShadowSize, bevel->top);
-
- /* draw lower bevel */
- RGBForeColor(&bevelLowerColor);
- PenSize(kBevelSize, kBevelSize);
- MoveTo(bevel->right - kBevelSize, bevel->top);
- LineTo(bevel->right - kBevelSize, bevel->bottom - kBevelSize);
- LineTo(bevel->left, bevel->bottom - kBevelSize);
-
- /* draw corners of bevel */
- RGBForeColor(&bevelCornerColor);
- PenSize(kBevelSize, kBevelSize);
- MoveTo(bevel->right - kBevelSize, bevel->top);
- LineTo(bevel->right - kBevelSize, bevel->top);
- MoveTo(bevel->left, bevel->bottom - kBevelSize);
- LineTo(bevel->left, bevel->bottom - kBevelSize);
- }
-
- /* draw the frame and shadow around the popup menu */
- static void PopupDrawFrame(PopupHandle popup)
- {
- Rect frame;
- Rect erase;
- PenState pen;
- ColorState color;
- Boolean partHasBackColor;
-
- require((**popup).state.drawSetup);
-
- /* save state */
- GetPenState(&pen);
- GetColorState(&color);
-
- /* calculate frame rectangle */
- PopupFrameRectangle(popup, &frame, false);
-
- /* draw upper part of frame */
- (void) PopupPartColorSet(popup, kPopupPartFrame);
- PenSize(kFrameSize, kFrameSize);
- MoveTo(frame.left, frame.bottom - kFrameSize - kShadowSize);
- LineTo(frame.left, frame.top);
- LineTo(frame.right - kFrameSize - kShadowSize, frame.top);
-
- /* draw lower part of frame (we could use a different color here) */
- (void) PopupPartColorSet(popup, kPopupPartFrame);
- PenSize(kFrameSize, kFrameSize);
- MoveTo(frame.right - kFrameSize, frame.top);
- LineTo(frame.right - kFrameSize, frame.bottom - kFrameSize);
- LineTo(frame.left, frame.bottom - kFrameSize);
-
- /* draw bevel */
- if ((**popup).attr.bevel && PopupPixelDepth(popup) >= kBevelDepth) {
- Rect bevel = frame;
- InsetRect(&bevel, kFrameSize, kFrameSize);
- PopupDrawBevel(popup, &bevel);
- }
-
- /* draw drop shadow */
- (void) PopupPartColorSet(popup, kPopupPartShadow);
- PenSize(kShadowSize, kShadowSize);
- MoveTo(frame.right, frame.top + kShadowOffset);
- LineTo(frame.right, frame.bottom);
- LineTo(frame.left + kShadowOffset, frame.bottom);
-
- /* erase the few pixels of the shadow that aren't drawn */
- partHasBackColor = ((PopupPartColorSet(popup, kPopupPartDefault) & kPartColorBack) != 0);
- erase.top = frame.top;
- erase.left = frame.right;
- erase.bottom = frame.top + kShadowOffset;
- erase.right = frame.right + kShadowSize;
- RectErase(&erase, partHasBackColor);
- erase.top = frame.bottom;
- erase.left = frame.left;
- erase.bottom = frame.bottom + kShadowSize;
- erase.right = frame.left + kShadowOffset;
- RectErase(&erase, partHasBackColor);
-
- /* restore state */
- SetPenState(&pen);
- SetColorState(&color);
- }
-
- /* draw the triangle */
- static void PopupDrawTriangle(PopupHandle popup)
- {
- Rect triangle; /* triangle's rectangle */
- short width; /* width of current line */
- short height; /* height of triangle */
- short i; /* index to lines of triangle */
- PenState penState;
- ColorState colorState;
-
- require((**popup).state.drawSetup);
-
- /* reset environment */
- GetPenState(&penState);
- GetColorState(&colorState);
- PenNormal();
-
- /* erase triangle */
- triangle = (**popup).r.triangle;
- RectErase(&triangle, (PopupPartColorSet(popup, kPopupPartItem) & kPartColorBack) != 0);
-
- /* draw triangle */
- (void) PopupPartColorSet(popup, kPopupPartTriangle);
- height = triangle.bottom - triangle.top;
- width = triangle.right - triangle.left;
- check(width % 2 == 0);
- for (i = 0; width >= 0; i++) {
- MoveTo(triangle.left + i, triangle.top + i);
- LineTo(triangle.left + i + width, triangle.top + i);
- width -= 2;
- }
-
- #if DRAW_SHADED_TRIANGLE
-
- /* draw shaded triangle */
- if ((**popup).attr.bevel && PopupPixelDepth(popup) >= kBevelDepth) {
- struct { long red, green, blue; } decrColor; /* decrement for drawing triangle */
- RGBColor lightColor; /* color of lightest part of triangle */
- RGBColor darkColor; /* color of darkest part of triangle */
- RGBColor lineColor; /* color of current line */
- RGBColor whiteColor;
- RGBColor dummyColor;
- short dummyMode;
-
- /* get colors, for calculating shading of triangle */
- (void) PopupPartColor(popup, kPopupPartFrame, &darkColor, &dummyColor, &dummyMode);
- (void) PopupPartColor(popup, kPopupPartBlackAndWhite, &dummyColor, &whiteColor, &dummyMode);
- PopupGetGray(popup, &darkColor, &whiteColor, &lightColor);
- PopupGetGray(popup, &lightColor, &whiteColor, &lightColor);
-
- /* calculate start color and increment for shaded triangle */
- #ifdef OUTLINE_TRIANGLE
- /* compile these statements to draw the shading within
- an outline of the triangle (IMHO, less aesthetic) */
- InsetRect(&triangle, 1, 1); triangle.left++;
- #endif /* OUTLINE_TRIANGLE */
- width = triangle.right - triangle.left;
- if (width > 0) {
- decrColor.red = ((long) lightColor.red - darkColor.red) / width;
- decrColor.green = ((long) lightColor.green - darkColor.green) / width;
- decrColor.blue = ((long) lightColor.blue - darkColor.blue) / width;
- lineColor = lightColor;
- if (decrColor.red || decrColor.green || decrColor.blue) {
-
- /* draw shading inside triangle */
- for (i = 0; i < width; i++) {
- RGBForeColor(&lineColor);
- lineColor.red -= decrColor.red;
- lineColor.green -= decrColor.green;
- lineColor.blue -= decrColor.blue;
- MoveTo(triangle.left + i, triangle.top);
- LineTo(triangle.left + i - i / 2, triangle.top + i / 2);
- }
- }
- }
- }
-
- #endif /* DRAW_SHADED_TRIANGLE */
-
- /* restore port */
- SetColorState(&colorState);
- SetPenState(&penState);
- }
-
- /* draw the string within the bounding rectangle; if the string is too long,
- it is truncated and an elipses character is appended */
- static void PopupDrawString(PopupHandle popup, Str255 str,
- const Rect *bounds, short just)
- {
- #pragma unused(popup)
- FontInfo font;
- short width;
-
- require((**popup).state.drawSetup);
- require(RectValid(bounds));
- GetFontInfo(&font);
- width = TruncatePString(str, bounds->right - bounds->left);
- if (just == teFlushRight)
- MoveTo(bounds->right - width, bounds->bottom - font.descent);
- else if (just == teCenter)
- MoveTo(bounds->left + (bounds->right - bounds->left - width) / 2, bounds->bottom - font.descent);
- else
- MoveTo(bounds->left, bounds->bottom - font.descent);
- DrawString(str);
- }
-
- /* draw the current selection string and icon */
- static void PopupDrawSelection(PopupHandle popup)
- {
- PopupDrawSelectionStructure saveState; /* saved state */
- Str255 itemString; /* string of currently selected menu item */
- FontInfo font; /* information about the current font */
- Rect selectionRect; /* rectangle to draw string in */
- short selectionHeight; /* height of current selection rectangle */
- short lineHeight; /* height of a line of text */
- short tweak; /* for tweaking position if not using Chicago */
- Rect iconRect; /* rectangle to draw icon in */
- Point iconSize; /* size of current item's icon */
- ResType iconType; /* resource type of current item's icon */
- short iconID; /* resource ID of current item's icon */
- Handle iconHandle; /* handle to current item's icon */
- SignedByte iconState; /* state of handle to icon */
- Boolean iconDraw; /* true if should draw the icon */
- IconTransformType iconTransform; /* transformation to apply when drawing icon */
-
- require((**popup).state.drawSetup);
-
- /* set up drawing environment */
- PopupPortSelectionSetup(popup, &saveState);
-
- /* initially, set the rectangle within which to draw the string
- equal to the entire current selection rectangle and erase it
- to the background color */
- selectionRect = (**popup).r.selection;
- RectErase(&selectionRect, (PopupPartColorSet(popup, kPopupPartItem) & kPartColorBack) != 0);
-
- if ((**popup).attr.typeIn) {
- /* don't draw selection contents for type-in style popup menu */
- PopupPortSelectionRestore(popup, &saveState);
- return;
- }
-
- GetFontInfo(&font);
- lineHeight = font.ascent + font.descent + font.leading;
-
- /* calculate size of current item's icon */
- MenuItemIcon((**popup).menu, (**popup).state.current,
- &iconType, &iconID, &iconSize);
-
- /* adjust tweak value if drawing in a font other than Chicago */
- tweak = 0;
- if ((**popup).attr.windowFont && (**popup).port->txFont != systemFont)
- tweak = 2;
-
- /* exclude mark character from selection rectangle */
- if (PopupFontJust(popup) == teFlushRight)
- selectionRect.right -= font.widMax + tweak;
- else
- selectionRect.left += font.widMax + tweak;
-
- /* exclude triangle from selection rectangle */
- if (PopupFontJust(popup) == teFlushRight)
- selectionRect.left += (**popup).r.triangle.right - (**popup).r.triangle.left + kTriangleMargin;
- else
- selectionRect.right -= (**popup).r.triangle.right - (**popup).r.triangle.left + kTriangleMargin;
-
- /* fix selection rectangle */
- if (selectionRect.right < selectionRect.left)
- selectionRect.right = selectionRect.left;
- if (selectionRect.left > selectionRect.right)
- selectionRect.left = selectionRect.right;
- check(RectValid(&selectionRect));
-
- /* get the rectangle for later calculating the icon rectangle */
- iconRect = selectionRect;
-
- /* exclude icon from selection rectangle */
- if (iconSize.h > 0) {
- if (PopupFontJust(popup) == teFlushRight)
- selectionRect.right -= iconSize.h + CharWidth(' ');
- else
- selectionRect.left += iconSize.h + CharWidth(' ');
- }
-
- /* align the bounding box within which to draw the current selection
- string with the base line of the title string and with the edge
- of the current selection rectangle */
- selectionHeight = selectionRect.bottom - selectionRect.top;
- check(selectionHeight >= lineHeight);
- selectionRect.top += (selectionHeight - lineHeight) / 2 - 1;
- selectionRect.bottom = selectionRect.top + lineHeight;
-
- /* fix selection rectangle */
- if (selectionRect.right < selectionRect.left)
- selectionRect.right = selectionRect.left;
- if (selectionRect.left > selectionRect.right)
- selectionRect.left = selectionRect.right;
- check(RectValid(&selectionRect));
-
- /* set the item's color */
-
- /* draw the item's string */
- (void) PopupPartColorSet(popup, kPopupPartItem);
- GetMenuItemText((**popup).menu, (**popup).state.current, itemString);
- PopupDrawString(popup, itemString, &selectionRect, PopupFontJust(popup));
-
- /* calculate the icon's rectangle, but don't draw the icon if
- it overlaps the item's text or the triangle */
- // program_note: how is the system MDEF calculating the positions of non-cicn icons???
- iconDraw = true;
- check(iconRect.bottom - iconRect.top >= iconSize.v);
- iconRect.top += (iconRect.bottom - iconRect.top - iconSize.v) / 2;
- iconRect.bottom = iconRect.top + iconSize.v;
- if (PopupFontJust(popup) == teFlushRight) {
- iconRect.left = iconRect.right - iconSize.h;
- if (iconRect.left < selectionRect.right ||
- iconRect.left < (**popup).r.triangle.right)
- {
- iconDraw = false;
- }
- }
- else {
- iconRect.right = iconRect.left + iconSize.h;
- if (iconRect.right > selectionRect.left ||
- iconRect.right > (**popup).r.triangle.left)
- {
- iconDraw = false;
- }
- }
-
- /* determine transformation to apply when drawing icon */
- iconTransform = ttNone;
- if (! (**popup).attr.enabled ||
- ! MenuItemEnabled((**popup).menu, (**popup).state.current))
- {
- /* We paint over the menu with a gray pattern if the port depth
- is less than kDisableDepth, and this would interfere with the
- gray pattern applied when the ttDisabled mode is used. */
- if (PopupPixelDepth(popup) >= kDisableDepth)
- iconTransform = ttDisabled;
- }
-
- /* draw the item's icon */
- if (iconDraw && iconType) {
- switch (iconType) {
- case 'SICN':
- (void) PopupPartColorSet(popup, kPopupPartIcon);
- iconHandle = GetResource(iconType, iconID);
- if (iconHandle && *iconHandle) {
- iconState = HGetState(iconHandle);
- HNoPurge(iconHandle);
- if (MacHasIconUtilities())
- PlotSICNHandle(&iconRect, atNone, iconTransform, iconHandle);
- else
- PlotSICN(&iconRect, iconHandle);
- HSetState(iconHandle, iconState);
- }
- break;
- case 'ICON':
- (void) PopupPartColorSet(popup, kPopupPartIcon);
- iconHandle = GetResource(iconType, iconID);
- if (iconHandle && *iconHandle) {
- iconState = HGetState(iconHandle);
- HNoPurge(iconHandle);
- if (MacHasIconUtilities())
- PlotIconHandle(&iconRect, atNone, iconTransform, iconHandle);
- else
- PlotIcon(&iconRect, iconHandle);
- HSetState(iconHandle, iconState);
- }
- break;
- case 'cicn':
- check(MacHasColorQD());
- (void) PopupPartColorSet(popup, kPopupPartBlackAndWhite);
- iconHandle = (Handle) GetCIcon(iconID);
- if (iconHandle) {
- if (*iconHandle) {
- if (MacHasIconUtilities())
- PlotCIconHandle(&iconRect, atNone, iconTransform, (CIconHandle) iconHandle);
- else
- PlotCIcon(&iconRect, (CIconHandle) iconHandle);
- }
- DisposeCIcon((CIconHandle) iconHandle);
- }
- break;
- }
- }
-
- /* gray over entire selection if item is disabled and we're drawing
- into a black and white port */
- if (PopupPixelDepth(popup) < kDisableDepth &&
- ! MenuItemEnabled((**popup).menu, (**popup).state.current))
- {
- selectionRect = (**popup).r.selection;
- RectDisable(&selectionRect);
- }
-
- /* restore drawing environment */
- PopupPortSelectionRestore(popup, &saveState);
-
- }
-
- /* draw the title string */
- static void PopupDrawTitle(PopupHandle popup, Boolean invert)
- {
- Str255 title; /* title string */
- Rect rtitle; /* title's text rectangle */
- Rect rhilite; /* title's hilite rectangle */
- TextState saveText; /* save text state */
- ColorState saveColor; /* saved color state */
-
- require((**popup).state.drawSetup);
- require(! (**popup).attr.typeIn);
-
- /* save port */
- GetTextState(&saveText);
- GetColorState(&saveColor);
-
- /* get rectangles */
- rhilite = (**popup).r.hilite;
- rtitle = (**popup).r.title;
-
- /* set the title's colors */
-
- /* set the text style */
- TextFace((**popup).attr.title.style);
-
- /* draw the title */
- if ((PopupPartColorInvertSet(popup, kPopupPartTitle, invert) & kPartColorBack) != 0)
- RectErase(&rhilite, true);
- else
- RectErase(&rhilite, invert);
- PopupTitle(popup, title);
- PopupDrawString(popup, title, &rtitle, (**popup).attr.title.just);
-
- /* restore port's settings */
- SetColorState(&saveColor);
- SetTextState(&saveText);
- }
-
- /* gray out entire popup menu if it's disabled */
- static void PopupDrawEnabled(PopupHandle popup)
- {
- Rect bounds;
-
- require((**popup).state.drawSetup);
- if (PopupPixelDepth(popup) < kDisableDepth && ! (**popup).attr.enabled) {
- bounds = (**popup).r.bounds;
- RectDisable(&bounds);
- }
- }
-
- /* erase the popup menu */
- static void PopupErase(PopupHandle popup, Boolean all)
- {
- ColorState state;
- Rect maxboundsRect;
- Rect tmpRect;
- RgnHandle rgn1;
- RgnHandle rgn2;
- Boolean partHasBackColor;
-
- require((**popup).state.drawSetup);
- GetColorState(&state);
- partHasBackColor = ((PopupPartColorSet(popup, kPopupPartDefault) & kPartColorBack) != 0);
- maxboundsRect = (**popup).maxbounds;
- if (all)
- RectErase(&maxboundsRect, partHasBackColor);
- else {
- /* a bunch of region calculations to minimize
- the amount of flickering of the popup's
- frame */
- rgn1 = NewRgn();
- rgn2 = NewRgn();
- if (! rgn1 || ! rgn2)
- RectErase(&maxboundsRect, partHasBackColor);
- else {
- tmpRect = (**popup).maxbounds;
- RectRgn(rgn1, &tmpRect);
- PopupFrameRectangle(popup, &tmpRect, true);
- RectRgn(rgn2, &tmpRect);
- DiffRgn(rgn1, rgn2, rgn1);
- tmpRect = (**popup).r.hilite;
- RectRgn(rgn2, &tmpRect);
- DiffRgn(rgn1, rgn2, rgn1);
- RgnErase(rgn1, partHasBackColor);
- }
- if (rgn1) DisposeRgn(rgn1);
- if (rgn2) DisposeRgn(rgn2);
- }
- SetColorState(&state);
- }
-
- #if DRAW_RECTANGLES
-
- /* draw frames around the parts of the popup; this is useful when
- debugging */
- static void PopupDrawRectangles(PopupHandle popup)
- {
- struct {
- long maxbounds;
- long bounds;
- long frame;
- long shadow;
- long title;
- long hilite;
- long selection;
- long triangle;
- } partColor = {
- blackColor,
- blueColor,
- yellowColor,
- magentaColor,
- redColor,
- cyanColor,
- greenColor,
- blueColor,
- };
- PenState penState;
- long foreColor;
- Rect shadow;
- Rect frame;
- GrafPtr port;
-
- /* setup port */
- GetPort(&port);
- GetPenState(&penState);
- foreColor = port->fgColor;
- PenNormal();
-
- /* draw outline of frame and shadow */
- if (kShadowSize > 2 && kFrameSize > 2) {
-
- /* calculate frame */
- frame = (**popup).r.bounds;
- if (PopupFontJust(popup) == teFlushRight)
- frame.right = (**popup).r.selection.right + kFrameSize + kShadowSize;
- else
- frame.left = (**popup).r.selection.left - kFrameSize;
- frame.right -= kShadowSize;
- frame.bottom -= kShadowSize;
-
- /* draw outline of frame */
- ForeColor(partColor.frame);
- FrameRect(&frame);
- InsetRect(&frame, kFrameSize, kFrameSize);
- FrameRect(&frame);
- InsetRect(&frame, -kFrameSize, -kFrameSize);
-
- /* draw outline of shadow */
- ForeColor(partColor.shadow);
- shadow.top = frame.bottom;
- shadow.left = frame.left + kShadowOffset;
- shadow.bottom = shadow.top + kShadowSize;
- shadow.right = frame.right + kShadowSize;
- FrameRect(&shadow);
- shadow.top = frame.top + kShadowOffset;
- shadow.left = frame.right;
- shadow.bottom = frame.bottom + kShadowSize;
- shadow.right = shadow.left + kShadowSize;
- FrameRect(&shadow);
- }
-
- /* draw the other rectangles */
-
- ForeColor(partColor.maxbounds);
- frame = (**popup).maxbounds;
- FrameRect(&frame);
-
- ForeColor(partColor.bounds);
- frame = (**popup).r.bounds;
- FrameRect(&frame);
-
- ForeColor(partColor.title);
- frame = (**popup).r.title;
- FrameRect(&frame);
-
- ForeColor(partColor.hilite);
- frame = (**popup).r.hilite;
- FrameRect(&frame);
-
- ForeColor(partColor.selection);
- frame = (**popup).r.selection;
- FrameRect(&frame);
-
- ForeColor(partColor.triangle);
- frame = (**popup).r.triangle;
- FrameRect(&frame);
-
- /* restore port */
- SetPenState(&penState);
- ForeColor(foreColor);
- }
-
- #endif /* DRAW_RECTANGLES */
-
- /* draw the menu into the popup's port */
- static void PopupDrawDirect(PopupHandle popup)
- {
- PopupDrawStateType saveDrawState; /* save port settings */
-
- require(PopupValid(popup));
-
- /* setup drawing environment */
- PopupPortSetup(popup, &saveDrawState);
-
- /* erase the entire popup */
- PopupErase(popup, false);
-
- #if DRAW_POPUP
- /* draw all the parts of the popup */
- if (! (**popup).attr.typeIn)
- PopupDrawTitle(popup, false);
- PopupDrawSelection(popup);
- PopupDrawTriangle(popup);
- PopupDrawFrame(popup);
- PopupDrawEnabled(popup);
- #endif /* DRAW_POPUP */
-
- #if DRAW_RECTANGLES
- PopupDrawRectangles(popup);
- #endif /* DRAW_RECTANGLES */
-
- /* restore drawing environment */
- PopupPortRestore(popup, &saveDrawState);
- }
-
- /* callback for DeviceLoop function */
- static pascal void PopupDrawDeviceLoopCallBack(short depth, short flags,
- GDHandle device, long data)
- {
- #pragma unused(flags)
- PopupHandle popup;
-
- popup = (PopupHandle) data;
- (**popup).draw.gdevice = device;
- (**popup).draw.gdepth = depth;
- PopupDrawDirect(popup);
- }
-
- /* Draw the popup using a device loop. The 'drawRgn' parameter should be
- set to the region enclosing the popup's maxbound's rectangle, in
- global coordinates. For greater efficiency, the popup's draw region
- can be set to the intersection of the popup's bounding rectangle
- and its port's clip and visRgn. */
- static void PopupDrawDeviceLoop(PopupHandle popup)
- {
- RgnHandle drawRgn;
-
- #if GENERATINGCFM
- RoutineDescriptor drawProcRD = BUILD_ROUTINE_DESCRIPTOR(uppDeviceLoopDrawingProcInfo, PopupDrawDeviceLoopCallBack);
- DeviceLoopDrawingUPP uppDrawProc = &drawProcRD;
- #else
- DeviceLoopDrawingUPP uppDrawProc = PopupDrawDeviceLoopCallBack;
- #endif
-
- drawRgn = NewRgn();
- if (drawRgn) {
- CopyRgn((**popup).port->clipRgn, drawRgn);
- SectRgn((**popup).port->visRgn, drawRgn, drawRgn);
- }
- if (MacHasColorQD() && drawRgn)
- DeviceLoop(drawRgn, uppDrawProc, (long) popup, 0);
- else {
- (**popup).draw.gdevice = NULL;
- PopupDrawDirect(popup);
- }
- if (drawRgn)
- DisposeRgn(drawRgn);
- }
-
- /* Draw the popup to an offscreen bitmap, then copy the bitmap to the screen. */
- void PopupDraw(PopupHandle popup)
- {
- const RGBColor whiteRGBColor = { 65535, 65535, 65535 };
- const RGBColor blackRGBColor = { 0, 0, 0 };
- RGBColor saveForeColor; /* saved foreground color */
- RGBColor saveBackColor; /* saved background color */
- CGrafPtr saveCPort; /* saved graphics port */
- GDHandle saveGDH; /* saved graphics device */
- Boolean direct; /* true if should draw to onscreen port */
- GWorldPtr gworld; /* temporary pointer to offscreen graphics world */
- GDHandle gdevice; /* temporary pointer to graphics device of gworld */
- short gdepth; /* temporary storage for depth of graphics world */
- Rect popupRect; /* popup's bounding rectangle */
- GWorldFlags updateFlags; /* flags returned by UpdateGWorld */
-
- require(PopupValid(popup));
-
- /* don't draw popup if drawing is disabled */
- if ( ! (**popup).attr.draw)
- return;
-
- /* try to draw from the offscreen graphics world */
- direct = true;
- (**popup).draw.gdepth = 0;
- (**popup).draw.gdevice = NULL;
- if ((**popup).draw.gworld) {
-
- /* use an offscreen graphics world */
- check(MacHasGWorlds());
-
- /* get the popup's bounding rectangle, in global coordinates */
- popupRect = (**popup).maxbounds;
- RectLocalToGlobal(&popupRect);
-
- /* Determine if the image spans multiple monitors with different
- pixel depths. If so, then using CopyBits to draw the image from
- an offscreen bitmap causes the image to be displayed poorly
- on the monitor with the smaller pixel depth. In particular,
- on a 1-bit screen, colors are mapped to black, and a disabled popup
- (grayed out) may not be displayed at all. While there may be some
- trick to getting offscreen graphics worlds to work well in this
- situation, for now we just use a regular device loop to draw the
- image to each screen device. */
- direct = false;
- if (MacHasColorQD()) {
- GDHandle mindevice;
- GDHandle maxdevice;
- mindevice = GetMinDevice(&popupRect);
- maxdevice = GetMaxDevice(&popupRect);
- if ( maxdevice &&
- mindevice &&
- mindevice != maxdevice &&
- GetDeviceDepth(mindevice) !=
- GetDeviceDepth(maxdevice))
- {
- direct = true;
- }
- }
-
- if (! direct) {
-
- /* assume we'll draw directly to the screen if anything goes wrong */
- direct = true;
-
- /* According to NIM:Imaging With QuickDraw, p6-9, we only
- need to call UpdateGWorld if the pixmap has been purged,
- after the window has been moved, and after update events.
- While it is simple enough to determine if the pixmap has
- been purged or if the window has been moved, it is not
- easy for a CDEF to determine when an update event has
- occurred. So, we always call UpdateGWorld whenever the
- popup is redrawn. It doesn't hurt (except for a possible
- performance hit) to call UpdateGWorld more than necessary. */
-
- /* update the offscreen graphics world */
- gworld = (**popup).draw.gworld;
- updateFlags = UpdateGWorld(&gworld, 0, &popupRect, NULL, NULL, 0);
- if ((updateFlags & gwFlagErr) == 0) {
- (**popup).draw.gworld = gworld;
-
- /* lock the offscreen pixmap */
- if (LockPixels(GetPixMap(gworld))) {
-
- /* we're drawing offscreen */
- direct = false;
-
- /* get the popup's bounding rectangle, in local coordinates */
- popupRect = (**popup).maxbounds;
-
- /* set the gworld's origin to coincide with the popup's origin */
- GetGWorld(&saveCPort, &saveGDH);
- SetGWorld(gworld, NULL);
- SetOrigin(popupRect.left, popupRect.top);
- check(EqualRect(&gworld->portRect, &popupRect));
-
- #if COPY_BACKPAT
- /* copy the popup port's background pixpat to the offscreen graphics world */
- if (GetPortBackPixPat((**popup).port)) {
- PixPatHandle bkPixPat = GetPortBackPixPat((**popup).port);
- if ((**bkPixPat).patType != 0)
- BackPixPat(bkPixPat);
- else {
- Pattern bkpat = (**bkPixPat).pat1Data;
- BackPat(&bkpat);
- }
- }
- else
- BackPat(&(**popup).port->bkPat);
- #endif /* COPY_BACKPAT */
-
- /* draw the popup to the offscreen graphics world */
- gdevice = GetGWorldDevice(gworld);
- gdepth = (gdevice ? GetDeviceDepth(gdevice) : 1);
- (**popup).draw.gdevice = gdevice;
- (**popup).draw.gdepth = gdepth;
- ClipRect(&popupRect);
- EraseRect(&popupRect);
- PopupDrawDirect(popup);
-
- /* setup port for CopyBits */
- SetGWorld(saveCPort, saveGDH);
- SetPort((**popup).port);
-
- /* use black and white to avoid colorization by CopyBits */
- if (MacHasColorQD()) {
- GetForeColor(&saveForeColor);
- GetBackColor(&saveBackColor);
- RGBForeColor(&blackRGBColor);
- RGBBackColor(&whiteRGBColor);
- }
-
- /* copy the offscreen pixmap to the onscreen port */
- check(EqualRect(&gworld->portRect, &popupRect));
- CopyBits(
- &((GrafPtr) gworld)->portBits,
- &(**popup).port->portBits,
- &popupRect, &popupRect,
- srcCopy, NULL);
-
- /* restore colors */
- RGBForeColor(&saveForeColor);
- RGBBackColor(&saveBackColor);
-
- /* reset offscreen origin for better performance on next UpdateGWorld */
- SetGWorld(gworld, NULL);
- SetOrigin(0, 0);
-
- /* restore port */
- SetGWorld(saveCPort, saveGDH);
-
- /* unlock the offscreen pixmap */
- UnlockPixels(GetPixMap(gworld));
- }
- }
- }
- }
-
- /* If we couldn't allocate an offscreen graphics world, or conditions
- weren't appropriate for drawing using the offscreen graphics world,
- we can still draw the popup directly to the onscreen port. */
- if (direct)
- PopupDrawDeviceLoop(popup);
- }
-
- /* hilite the popup's title; useful for keyboard equivalents of commands */
- static void PopupHilite(PopupHandle popup, Boolean invert)
- {
- Rect hilite;
-
- require(PopupValid(popup));
- require((**popup).state.drawSetup);
- if ((**popup).attr.draw) {
- hilite = (**popup).r.hilite;
- if (! EmptyRect(&hilite)) {
- if (PopupPortIsColor(popup))
- PopupDrawTitle(popup, invert);
- else
- InvertRect(&hilite);
- }
- }
- }
-
- /*-------------------------------------------------------------------------*/
- /* handling clicks in the popup menu */
- /*-------------------------------------------------------------------------*/
-
- /* A handle to this structure is installed in the menu's menuProc field. */
- typedef struct {
- PopupGlueUnion glue; /* glue stuff */
- Handle proc; /* original MDEF handle */
- short width; /* width of menu */
- } PatchMenuProcStructure, **PatchMenuProcHandle;
-
- /* Menu proc to patch the mSizeMsg, allowing us to set the menu width to
- include the triangle. This is installed just before calling
- PopupMenuSelect, and is removed immediately afterwards. */
- static pascal void PatchMenuProc(short message, MenuHandle menu,
- Rect *menuRect, Point hitPt, short *whichItem)
- {
- PatchMenuProcHandle patch;
- SignedByte originalState;
- Handle originalProc;
- Handle saveProc;
-
- patch = (PatchMenuProcHandle) (**menu).menuProc;
- saveProc = (**menu).menuProc;
- originalProc = (**patch).proc;
- originalState = HGetState(originalProc);
- HLock(originalProc);
- (**menu).menuProc = originalProc;
- CallMenuDefProc((MenuDefProcPtr) *originalProc, message, menu, menuRect, hitPt, whichItem);
- (**menu).menuProc = saveProc;
- if (message == mSizeMsg)
- (**menu).menuWidth = (**patch).width;
- HSetState(originalProc, originalState);
- }
-
- /* True if point (in local coordinates) is within the popup menu.
- Call this before calling PopupSelect. */
- Boolean PopupWithin(PopupHandle popup, Point pt)
- {
- Rect hilite;
- Rect selection;
- Rect maxbounds;
- Boolean result;
-
- require(PopupValid(popup));
- result = false;
- if ((**popup).attr.enabled) {
- hilite = (**popup).r.hilite;
- selection = (**popup).r.selection;
- maxbounds = (**popup).maxbounds;
- result = (PtInRect(pt, &maxbounds) &&
- (PtInRect(pt, &selection) || PtInRect(pt, &hilite)));
- }
- return(result);
- }
-
- /* call this when there's a mouse down in a popup menu */
- short PopupSelect(PopupHandle popup)
- {
- long chosen; /* item selected from menu */
- Point location; /* top left of menu */
- short oldMenuWidth; /* saved menu width */
- Handle oldMenuProc; /* saved menuProc */
- short menuWidth; /* width of menu */
- PopupDrawStateType saveDrawState; /* saved port settings */
- short part; /* part selected in control */
-
- require(PopupValid(popup));
- LMSetMenuDisable(0);
- part = kPopupPartNotSelected;
- if (StillDown()) {
-
- /* setup port */
- PopupPortSetup(popup, &saveDrawState);
-
- /* hilite title */
- PopupHilite(popup, true);
-
- /* adjust width of menu */
- CalcMenuSize((**popup).menu);
- oldMenuWidth = menuWidth = (**(**popup).menu).menuWidth;
- if (! (**popup).attr.typeIn) {
-
- /* make the menu wide enough to include the triangle */
- menuWidth += (**popup).r.triangle.right - (**popup).r.triangle.left + kTriangleMargin;
- if ((**popup).attr.fixedWidth) {
- /* make menu fill all of current selection rectangle */
- menuWidth = (**popup).r.selection.right - (**popup).r.selection.left;
- }
- if (menuWidth < oldMenuWidth)
- menuWidth = oldMenuWidth;
-
- /* Though we patch the menu's mdef to ignore the width calculated
- by the mSizeMsg message, we still need to explicitely set the
- menu's width. This is necessary since PopupMenuSelect on
- non-color macs (e.g., a Plus running system 7) doesn't send
- the mSizeMsg message before drawing the menu, while on other
- macs (e.g., a Quadra running system 7 Pro) the mSizeMsg is
- sent by PopupMenuSelect. */
- (**(**popup).menu).menuWidth = menuWidth;
- }
-
- /* patch the menu's mdef to ignore the width calculated by
- the mSizeMsg message */
- oldMenuProc = (**(**popup).menu).menuProc;
- if ((**popup).menuProc) {
- PatchMenuProcHandle patch = (PatchMenuProcHandle) (**popup).menuProc;
- PopupGlueInit(&(**patch).glue, (ProcPtr) PatchMenuProc, uppMenuDefProcInfo);
- (**(**popup).menu).menuProc = (**popup).menuProc;
- (**patch).proc = oldMenuProc;
- (**patch).width = menuWidth;
- }
-
- /* calculate position for popup menu */
- location.v = (**popup).r.selection.top;
- if (PopupFontJust(popup) == teJustRight)
- location.h = (**popup).r.selection.right - menuWidth;
- else
- location.h = (**popup).r.selection.left;
- LocalToGlobal(&location);
-
- /* let user select an item from the menu */
- chosen = PopUpMenuSelect((**popup).menu, location.v, location.h,
- (**popup).state.current);
-
- /* unhilite the menu */
- PopupHilite(popup, false);
-
- /* restore environment */
- (**(**popup).menu).menuProc = oldMenuProc;
- (**(**popup).menu).menuWidth = oldMenuWidth;
- PopupPortRestore(popup, &saveDrawState);
-
- /* display the selected item */
- if (HiWord(chosen)) {
- part = kPopupPartSelected;
- PopupCurrentSet(popup, LoWord(chosen));
- }
- }
- ensure(PopupValid(popup));
- return(part);
- }
-
- /*-------------------------------------------------------------------------*/
- /* getting and setting attributes */
- /*-------------------------------------------------------------------------*/
-
- /* return the version of the library that created the popup menu */
- short PopupVersion(PopupHandle popup)
- {
- return((**popup).version);
- }
-
- /* return the currently selected menu item */
- short PopupCurrent(PopupHandle popup)
- {
- require(PopupValid(popup));
- return((**popup).state.current);
- }
-
- /* set the currently selected menu item */
- void PopupCurrentSet(PopupHandle popup, short current)
- {
- short item, nitems;
-
- require(PopupValid(popup));
- nitems = CountMItems((**popup).menu);
- for (item = 1; item <= nitems; item++)
- SetItemMark((**popup).menu, item, noMark);
- SetItemMark((**popup).menu, current, (**popup).attr.mark);
- (**popup).state.current = current;
- }
-
- /* turn drawing on or off */
- void PopupDrawSet(PopupHandle popup, Boolean draw)
- {
- require(PopupValid(popup));
- (**popup).attr.draw = draw;
- }
-
- /* enable or disable the menu */
- void PopupEnableSet(PopupHandle popup, Boolean enabled)
- {
- require(PopupValid(popup));
- (**popup).attr.enabled = enabled;
- }
-
- /* set the character used to mark the current menu item */
- void PopupMarkSet(PopupHandle popup, char mark)
- {
- require(PopupValid(popup));
- SetItemMark((**popup).menu, (**popup).state.current, mark);
- (**popup).attr.mark = mark;
- }
-
- /* turn type-in style popup menu on or off */
- void PopupTypeInSet(PopupHandle popup, Boolean typeIn)
- {
- require(PopupValid(popup));
- (**popup).attr.typeIn = typeIn;
- }
-
- /* turn bevel drawing on or off */
- void PopupBevelSet(PopupHandle popup, Boolean bevel)
- {
- require(PopupValid(popup));
- (**popup).attr.bevel = bevel;
- }
-
- /* return rectangle enclosing all of popup; this is the rectangle
- enclosing the parts of the popup that are actually visible, and
- may be smaller than the rectangle specified with PopupBoundsSet. */
- void PopupBounds(PopupHandle popup, Rect *bounds)
- {
- require(PopupValid(popup));
- *bounds = (**popup).r.bounds;
- ensure(RectValid(bounds));
- }
-
- /* set popup's maximum bounding rectangle; all drawing is clipped
- to this rectangle */
- void PopupBoundsSet(PopupHandle popup, const Rect *maxbounds)
- {
- PopupDrawStateType saveDrawState;
- Rect oldmaxbounds;
-
- require(PopupValid(popup));
- require(RectValid(maxbounds));
- oldmaxbounds = (**popup).maxbounds;
- if (! EqualRect(&oldmaxbounds, maxbounds)) {
- if ((**popup).attr.draw) {
- PopupPortSetup(popup, &saveDrawState);
- PopupErase(popup, true);
- PopupPortRestore(popup, &saveDrawState);
- }
- (**popup).maxbounds = *maxbounds;
- }
- }
-
- /* return popup's title string */
- void PopupTitle(PopupHandle popup, Str255 title)
- {
- *title = 0;
- if ((**popup).attr.title.string) {
- BlockMoveData(*(**popup).attr.title.string, title,
- **(**popup).attr.title.string + 1);
- }
- }
-
- /* set popup's title string */
- void PopupTitleSet(PopupHandle popup, const Str255 title)
- {
- Str255 oldtitle;
-
- require(PopupValid(popup));
- if ((**popup).attr.title.string) {
- PopupTitle(popup, oldtitle);
- if (! EqualString(title, oldtitle, true, true))
- PtrToXHand(title, (**popup).attr.title.string, *title + 1);
- }
- }
-
- /* set width of popup's title; the title is resized dynamically
- if the width is zero */
- void PopupTitleWidthSet(PopupHandle popup, short width)
- {
- require(PopupValid(popup));
- require(width >= 0);
- (**popup).attr.title.width = width;
- }
-
- /* set the text style in which the popup's title will be drawn */
- void PopupTitleStyleSet(PopupHandle popup, Style style)
- {
- require(PopupValid(popup));
- (**popup).attr.title.style = style;
- }
-
- /* set whether the popup will use the window's font for drawing the
- title, current selection, and menu */
- void PopupWindowFontSet(PopupHandle popup, Boolean wfont)
- {
- require(PopupValid(popup));
- (**popup).attr.windowFont = wfont;
- }
-
- /* set whether the popup will use a fixed width or will be resized
- dynamically */
- void PopupFixedWidthSet(PopupHandle popup, Boolean fixedWidth)
- {
- require(PopupValid(popup));
- (**popup).attr.fixedWidth = fixedWidth;
- }
-
- /* set the justification style for drawing the popup */
- void PopupJustSet(PopupHandle popup, short just)
- {
- require(PopupValid(popup));
- (**popup).attr.title.just = just;
- }
-
- /*-------------------------------------------------------------------------*/
- /* allocation and disposal */
- /*-------------------------------------------------------------------------*/
-
- /* Create a popup menu within the rectangle in the specified port.
- Drawing is initially off. Since the popup's title is initially empty,
- you should call PopupTitleSet if you want the popup to have a title.
- When you are finished configuring the popup, call PopupDrawSet to
- enable drawing and then call PopupCalculate. The popup's rectangles
- are only calculated when drawing is enabled. The popup menu allocates
- several utility handles, but will function, albeit not as well, even
- if it can't allocate any of the utility handles. To reduce flicker,
- the popup menu is drawn to an offscreen graphics world and then copied
- to the screen. The storage for the offscreen pixmap is kept in a
- relocatable and purgeable block. */
- PopupHandle PopupBegin(GrafPtr port,
- MenuHandle menu,
- const Rect *maxbounds,
- ControlHandle ctl)
- {
- PopupHandle popup;
- void *tmp;
-
- require(port != NULL);
- require(menu != NULL);
- require(ctl != NULL);
- require(RectValid(maxbounds));
-
- /* allocate popup */
- popup = (PopupHandle) NewHandleClear(sizeof(PopupType));
- if (popup) {
-
- /* initialize internal state */
- (**popup).privateData.mHandle = menu;
- (**popup).privateData.mID = (**menu).menuID;
- (**popup).version = kPopupVersion;
- (**popup).port = port;
- (**popup).menu = menu;
- (**popup).ctl = ctl;
- (**popup).maxbounds = *maxbounds;
- (**popup).attr.bevel = (DRAW_BEVEL != 0);
-
- /* initialize flags affecting display and operation of menu */
- (**popup).attr.enabled = true;
- (**popup).attr.mark = checkMark;
- #if FLUSH_RIGHT
- (**popup).attr.title.just = teFlushRight;
- #else
- (**popup).attr.title.just = PopupFontJust(popup);
- #endif
-
- /* allocate title */
- tmp = NewHandleClear(1);
- (**popup).attr.title.string = tmp;
-
- /* allocate glue handle for patching MDEF */
- tmp = NewHandle(sizeof(PatchMenuProcStructure));
- if (tmp)
- (**popup).menuProc = tmp;
-
- /* Allocate an offscreen graphics world. The offscreen pixmap is
- purgeable, since we can always rebuild the data it contains and
- since we can always draw the popup to the onscreen port, though
- the image may flicker a bit. */
- #if DRAW_OFFSCREEN
- if (MacHasGWorlds()) {
- Rect globalBounds;
- GWorldPtr gworld;
- GrafPtr savePort;
- GetPort(&savePort);
- SetPort((**popup).port);
- globalBounds = (**popup).maxbounds;
- RectLocalToGlobal(&globalBounds);
- if (NewGWorld(&gworld, 0, &globalBounds, NULL, NULL, pixPurge) == noErr)
- (**popup).draw.gworld = gworld;
- SetPort(savePort);
- }
- #endif /* DRAW_OFFSCREEN */
- }
- ensure(! popup || PopupValid(popup));
- return(popup);
- }
-
- /* dispose of the popup menu */
- void PopupEnd(PopupHandle popup)
- {
- require(! popup || PopupValid(popup));
- if (popup) {
- if ((**popup).menuProc) DisposeHandle((**popup).menuProc);
- if ((**popup).draw.gworld) DisposeGWorld((**popup).draw.gworld);
- if ((**popup).attr.title.string) DisposeHandle((**popup).attr.title.string);
- DisposeHandle((Handle) popup);
- popup = NULL;
- }
- ensure(! PopupValid(popup));
- }
-